@dtducas/wh-forge-viewer 1.0.7 → 1.0.9-beta0

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 CHANGED
@@ -1,92 +1,392 @@
1
1
  # wh-forge-viewer
2
2
 
3
- A customized Autodesk Forge Viewer component for React with enhanced PDF support and custom toolbar.
3
+ A professional Autodesk Forge Viewer component for React with advanced PDF support, comprehensive markup tools, and real-time collaboration capabilities.
4
4
 
5
5
  ## Features
6
6
 
7
- - **Custom Toolbar** - Pre-configured toolbar with Pan, Document Browser, Download, and Pagination controls
8
- - ✅ **PDF Support** - Optimized for viewing PDF documents with page navigation
9
- - **Auto Document Browser** - Automatically opens on startup, positioned on left side
10
- - **Smooth Pagination** - Page navigation without toolbar flickering
11
- - **Self-Healing** - Automatic toolbar recovery mechanism ensures UI persistence
12
- - **3D/2D Models** - Full support for Autodesk Forge 3D and 2D models
13
- - **React Integration** - Easy-to-use React component with hooks support
14
- - ✅ **SOLID Architecture** - Follows SOLID principles with Manager pattern for maintainability
7
+ ### Core Viewer
8
+
9
+ - **Custom Toolbar** - Pan, Document Browser, Download, and Pagination controls
10
+ - **Enhanced PDF Support** - Optimized for viewing PDF documents with intelligent page navigation
11
+ - **Smart Document Browser** - Auto-opens with adaptive panel sizing
12
+ - **3D/2D Models** - Full support for DWF, DWFX formats
13
+ - **React Integration** - Easy-to-use React component with hooks support
14
+
15
+ ### Markup Tools
16
+
17
+ - **12 Markup Tools** - Arrow, Rectangle, Circle, Cloud, Text, Freehand, Line, Polyline, Polycloud, Callout, Image/Stamp
18
+ - **SelectTool with Multi-Select** - Drag rectangle to select multiple markups
19
+ - **Property Panel** - Excalidraw-inspired UI for editing markup properties
20
+ - **Undo/Redo** - Full undo/redo support for all operations
21
+ - **Custom Stamps** - Insert images as stamps with drag-drop support
22
+
23
+ ### Collaboration
24
+
25
+ - **Real-Time Sync** - Subscribe to markup changes for WebSocket broadcasting
26
+ - **User Presence** - Track and display remote user cursors
27
+ - **Persistence API** - Save/load markups with JSON or SVG formats
28
+ - **Page Change Events** - Handle markup data during page navigation
29
+
30
+ ### Architecture
31
+
32
+ - **SOLID Principles** - Manager pattern with clear separation of concerns
33
+ - **Type Safety** - Full TypeScript definitions included
34
+ - **Event-Driven** - Decoupled communication via EventBus
35
+ - **Extensible** - Factory pattern for adding custom tools
15
36
 
16
37
  ## Installation
17
38
 
18
39
  ```bash
19
- npm install wh-forge-viewer
40
+ npm install @dtducas/wh-forge-viewer
20
41
  ```
21
42
 
22
- ## Usage
43
+ ## Quick Start
23
44
 
24
- ```jsx
25
- import { ViewerForgePDF } from 'wh-forge-viewer';
45
+ ### Basic Viewer
26
46
 
27
- function MyApp() {
28
- const [viewer, setViewer] = useState(null);
47
+ ```jsx
48
+ import { ViewerForgePDF } from '@dtducas/wh-forge-viewer';
29
49
 
50
+ function App() {
30
51
  return (
31
52
  <ViewerForgePDF
32
53
  filePath='https://example.com/document.pdf'
33
54
  fileExt='pdf'
34
- setViewer={setViewer}
55
+ onViewerReady={(viewer) => console.log('Ready:', viewer)}
35
56
  />
36
57
  );
37
58
  }
38
59
  ```
39
60
 
40
- ## Props
61
+ ### With Markup Tools
41
62
 
42
- | Prop | Type | Required | Description |
43
- | ----------- | -------- | -------- | ------------------------------------- |
44
- | `filePath` | string | Yes | URL to the document to load |
45
- | `fileExt` | string | Yes | File extension (pdf, dwf, dwfx, etc.) |
46
- | `setViewer` | function | No | Callback to receive viewer instance |
63
+ ```jsx
64
+ import {
65
+ ViewerForgePDF,
66
+ DrawingManager,
67
+ MarkupToolbar,
68
+ PropertyPanel,
69
+ EventBus,
70
+ } from '@dtducas/wh-forge-viewer';
71
+
72
+ function MarkupViewer() {
73
+ const [viewer, setViewer] = useState(null);
74
+ const [drawingManager, setDrawingManager] = useState(null);
75
+ const [activeTool, setActiveTool] = useState(null);
76
+ const [propertyConfig, setPropertyConfig] = useState(null);
77
+ const [propertyValues, setPropertyValues] = useState({});
47
78
 
48
- ## Custom Toolbar
79
+ const eventBus = EventBus.getInstance();
49
80
 
50
- The viewer includes a custom toolbar with:
81
+ useEffect(() => {
82
+ if (!viewer) return;
51
83
 
52
- ### Tools Group
84
+ const manager = new DrawingManager(viewer, eventBus, {
85
+ autoEnterEditMode: true,
86
+ defaultTool: 'arrow',
87
+ });
53
88
 
54
- - **Pan** - Enable pan navigation
55
- - **Document Browser** - Toggle document tree view
56
- - **Download** - Download the current document
89
+ setDrawingManager(manager);
57
90
 
58
- ### Pagination Group
91
+ return () => manager.destroy();
92
+ }, [viewer]);
59
93
 
60
- - **Previous Page** - Navigate to previous page
61
- - **Page Counter** - Display current page / total pages
62
- - **Next Page** - Navigate to next page
94
+ const handleToolChange = (tool) => {
95
+ drawingManager?.setActiveTool(tool);
96
+ setActiveTool(tool);
97
+ setPropertyConfig(drawingManager?.getCurrentToolPropertyConfig());
98
+ setPropertyValues(drawingManager?.getCurrentToolPropertyValues() || {});
99
+ };
63
100
 
64
- ## Requirements
101
+ return (
102
+ <div style={{ position: 'relative', width: '100%', height: '100vh' }}>
103
+ <ViewerForgePDF
104
+ filePath='/document.pdf'
105
+ fileExt='pdf'
106
+ onViewerReady={setViewer}
107
+ />
108
+
109
+ {drawingManager && (
110
+ <>
111
+ <MarkupToolbar
112
+ activeTool={activeTool}
113
+ onToolChange={handleToolChange}
114
+ onUndo={() => drawingManager.undo()}
115
+ onRedo={() => drawingManager.redo()}
116
+ />
117
+
118
+ <PropertyPanel
119
+ config={propertyConfig}
120
+ values={propertyValues}
121
+ onChange={(key, value) => {
122
+ drawingManager.handlePropertyChange(key, value);
123
+ setPropertyValues(drawingManager.getCurrentToolPropertyValues());
124
+ }}
125
+ />
126
+ </>
127
+ )}
128
+ </div>
129
+ );
130
+ }
131
+ ```
65
132
 
66
- - React ^17.0.0 || ^18.0.0
67
- - Ant Design ^5.0.0
68
- - Autodesk Forge Viewer v7.108.0 (pinned for stability)
133
+ ### Real-Time Collaboration
134
+
135
+ ```jsx
136
+ import {
137
+ DrawingManager,
138
+ EventBus,
139
+ CursorOverlay,
140
+ } from '@dtducas/wh-forge-viewer';
141
+
142
+ function CollaborativeViewer({ socket }) {
143
+ const [drawingManager, setDrawingManager] = useState(null);
144
+ const [remoteCursors, setRemoteCursors] = useState(new Map());
145
+
146
+ useEffect(() => {
147
+ if (!drawingManager) return;
148
+
149
+ // Subscribe to local markup changes
150
+ const unsubscribe = drawingManager.onMarkupChange((delta) => {
151
+ socket.emit('markup:delta', delta);
152
+ });
153
+
154
+ // Subscribe to local cursor movement
155
+ const unsubCursor = drawingManager.onLocalCursorMove((position) => {
156
+ socket.emit('cursor:move', { userId: currentUser.id, position });
157
+ });
158
+
159
+ // Handle remote changes
160
+ socket.on('markup:delta', (delta) => {
161
+ drawingManager.applyDelta(delta);
162
+ });
163
+
164
+ // Handle remote cursors
165
+ socket.on('cursor:move', (cursor) => {
166
+ drawingManager.updateUserCursor(cursor);
167
+ setRemoteCursors(new Map(drawingManager.getRemoteCursors()));
168
+ });
169
+
170
+ return () => {
171
+ unsubscribe();
172
+ unsubCursor();
173
+ };
174
+ }, [drawingManager, socket]);
175
+
176
+ return (
177
+ <>
178
+ {/* ... viewer components ... */}
179
+ <CursorOverlay cursors={remoteCursors} />
180
+ </>
181
+ );
182
+ }
183
+ ```
184
+
185
+ ### Persistence
186
+
187
+ ```javascript
188
+ // Save to localStorage
189
+ const data = drawingManager.getMarkupData();
190
+ localStorage.setItem('markups', JSON.stringify(data));
191
+
192
+ // Load from localStorage
193
+ const saved = JSON.parse(localStorage.getItem('markups'));
194
+ await drawingManager.loadMarkupData(saved, { mode: 'replace' });
195
+
196
+ // Save to database (JSON format)
197
+ const jsonData = drawingManager.getMarkupDataAsJson('page-guid-123');
198
+ await db.collection('markups').doc('page-guid-123').set(jsonData);
199
+
200
+ // Load from database
201
+ const doc = await db.collection('markups').doc('page-guid-123').get();
202
+ await drawingManager.loadMarkupDataFromJson(doc.data());
203
+ ```
204
+
205
+ ### PDF Export with Markup
206
+
207
+ ```javascript
208
+ // Export current page with markup as vector overlay
209
+ const result = await drawingManager.exportCurrentPagePDF(filePath, 0);
210
+ if (result.success && result.blob) {
211
+ const url = URL.createObjectURL(result.blob);
212
+ window.open(url); // Preview in new tab
213
+ }
214
+
215
+ // Multi-page export
216
+ const markupsByPage = [
217
+ { pageIndex: 0, svgString: savedPage0Svg },
218
+ { pageIndex: 1, svgString: drawingManager.getMarkupSVG() }, // current page
219
+ ];
220
+ await drawingManager.exportAndDownloadPDF(filePath, markupsByPage, {
221
+ filename: 'annotated-document',
222
+ });
223
+ ```
224
+
225
+ ## Components
226
+
227
+ ### ViewerForgePDF
228
+
229
+ Main viewer component.
230
+
231
+ | Prop | Type | Required | Description |
232
+ | ------------------ | -------- | -------- | ------------------------------- |
233
+ | `filePath` | string | Yes | URL to the document |
234
+ | `fileExt` | string | Yes | File extension (pdf, dwf, dwfx) |
235
+ | `onViewerReady` | function | No | Callback with viewer instance |
236
+ | `onDocumentLoaded` | function | No | Callback with viewables array |
237
+ | `onError` | function | No | Callback for errors |
238
+
239
+ ### MarkupToolbar
240
+
241
+ Floating toolbar for tool selection.
242
+
243
+ | Prop | Type | Description |
244
+ | -------------- | -------- | --------------------- |
245
+ | `activeTool` | string | Currently active tool |
246
+ | `onToolChange` | function | Tool change callback |
247
+ | `onUndo` | function | Undo callback |
248
+ | `onRedo` | function | Redo callback |
249
+
250
+ ### PropertyPanel
69
251
 
70
- ## Important Notes
252
+ Side panel for editing properties.
71
253
 
72
- ### Viewer Versioning
254
+ | Prop | Type | Description |
255
+ | ---------- | ------------------- | ------------------------ |
256
+ | `config` | IToolPropertyConfig | Property configuration |
257
+ | `values` | object | Current property values |
258
+ | `onChange` | function | Property change callback |
259
+ | `onAction` | function | Action button callback |
73
260
 
74
- This package uses a **specific version** of Autodesk Forge Viewer (v7.108.0) instead of wildcards (7.*) to ensure production stability and avoid unexpected breaking changes.
261
+ ### CursorOverlay
75
262
 
76
- **Why?** Using wildcards can introduce unexpected behavior changes when Autodesk releases updates. For production applications, always use a specific tested version.
263
+ Overlay for displaying remote cursors.
77
264
 
78
- 📖 See [docs/VERSIONING.md](docs/VERSIONING.md) for version update guide.
265
+ | Prop | Type | Description |
266
+ | -------------- | ------- | ----------------------------- |
267
+ | `cursors` | Map | Remote user cursors |
268
+ | `viewerBounds` | DOMRect | Viewer bounds for positioning |
79
269
 
80
- ## Architecture
270
+ ## DrawingManager API
81
271
 
82
- This package follows **SOLID principles** and uses a modular architecture:
272
+ ### Edit Mode
83
273
 
84
- - **Managers Pattern** - Separate managers for Pagination, Toolbar, and Document Browser
85
- - **Service Layer** - EventBus, FileLoader, DownloadService
86
- - **Type Safety** - Full TypeScript type definitions
87
- - **Dependency Injection** - Clean separation of concerns
274
+ ```typescript
275
+ await drawingManager.enterEditMode(); // Enter markup mode
276
+ drawingManager.leaveEditMode(); // Exit markup mode
277
+ drawingManager.isEditMode(); // Check if in edit mode
278
+ ```
279
+
280
+ ### Tools
281
+
282
+ ```typescript
283
+ drawingManager.setActiveTool('arrow'); // Set active tool
284
+ drawingManager.getActiveTool(); // Get current tool
285
+ drawingManager.setStrokeStyle('#e03131', 2, 1.0); // Set stroke
286
+ drawingManager.setFillStyle('#228be6', 0.3); // Set fill
287
+ ```
288
+
289
+ **Available Tools:**
290
+ `select`, `arrow`, `rectangle`, `ellipse`, `cloud`, `label`, `freehand`, `line`, `polyline`, `polycloud`, `callout`, `image`
291
+
292
+ ### Markup Operations
293
+
294
+ ```typescript
295
+ drawingManager.saveMarkups(); // Save current markups
296
+ drawingManager.loadMarkups(data); // Load markups
297
+ drawingManager.clearMarkups(); // Clear all markups
298
+ drawingManager.exportToSVG(); // Export to SVG string
299
+ drawingManager.undo(); // Undo last action
300
+ drawingManager.redo(); // Redo last undone action
301
+ ```
302
+
303
+ ### Collaborative API
304
+
305
+ ```typescript
306
+ // Get markup data for persistence
307
+ const data = drawingManager.getMarkupData(viewableGUID);
308
+
309
+ // Load markup data
310
+ await drawingManager.loadMarkupData(data, { mode: 'replace' });
311
+
312
+ // Subscribe to changes
313
+ const unsubscribe = drawingManager.onMarkupChange((delta) => {
314
+ // delta: { type: 'create'|'update'|'delete', markupId, data, timestamp }
315
+ socket.emit('markup:delta', delta);
316
+ });
317
+
318
+ // Apply remote changes
319
+ drawingManager.applyDelta(delta);
320
+
321
+ // Batch apply (for initial load)
322
+ drawingManager.applyBatch(elements, true);
323
+ ```
324
+
325
+ ### User Presence
326
+
327
+ ```typescript
328
+ // Broadcast local cursor
329
+ const unsub = drawingManager.onLocalCursorMove((position) => {
330
+ socket.emit('cursor:move', { userId, position });
331
+ });
332
+
333
+ // Update remote cursor
334
+ drawingManager.updateUserCursor({ userId, userName, color, position });
335
+
336
+ // Remove cursor when user leaves
337
+ drawingManager.removeUserCursor(userId);
338
+
339
+ // Get all remote cursors
340
+ const cursors = drawingManager.getRemoteCursors();
341
+ ```
342
+
343
+ ## EventBus Events
344
+
345
+ | Event | Payload | Description |
346
+ | -------------------------- | ------------------------ | ---------------------- |
347
+ | `page:changing` | `{ fromIndex, toIndex }` | Before page navigation |
348
+ | `page:changed` | `{ index, viewable }` | After page navigation |
349
+ | `drawing:editModeEntered` | `{}` | Entered markup mode |
350
+ | `drawing:editModeExited` | `{}` | Exited markup mode |
351
+ | `drawing:toolChanged` | `{ tool }` | Tool changed |
352
+ | `drawing:propertiesSynced` | `{ values }` | Properties updated |
88
353
 
89
- 📖 See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed architecture documentation.
354
+ ```javascript
355
+ import { EventBus, EVENT_NAMES } from '@dtducas/wh-forge-viewer';
356
+
357
+ const eventBus = EventBus.getInstance();
358
+
359
+ eventBus.on(EVENT_NAMES.PAGE_CHANGED, ({ index }) => {
360
+ console.log('Now on page:', index);
361
+ });
362
+ ```
363
+
364
+ ## Custom Toolbar
365
+
366
+ Built-in toolbar includes:
367
+
368
+ **Tools Group:**
369
+
370
+ - Pan Tool - Navigate the document
371
+ - Document Browser - Toggle thumbnail/tree view
372
+ - Download - Download current document
373
+
374
+ **Pagination Group:**
375
+
376
+ - Previous/Next Page navigation
377
+ - Current page indicator
378
+
379
+ **Smart Features:**
380
+
381
+ - No-refresh navigation when Document Browser is open
382
+ - Self-healing mechanism for toolbar persistence
383
+ - Synchronized button states
384
+
385
+ ## Requirements
386
+
387
+ - React ^17.0.0 || ^18.0.0
388
+ - Ant Design ^5.0.0 (peer dependency)
389
+ - Modern browsers (Chrome 90+, Firefox 88+, Safari 14+, Edge 90+)
90
390
 
91
391
  ## Development
92
392
 
@@ -99,16 +399,17 @@ npm run build
99
399
 
100
400
  # Watch mode
101
401
  npm run dev
102
- ```
103
402
 
104
- ## Publishing
403
+ # Test app
404
+ cd test/app && npm install && npm run dev
405
+ ```
105
406
 
106
- The package is automatically published to NPM when you push a version tag:
407
+ ## Documentation
107
408
 
108
- ```bash
109
- git tag v1.0.0
110
- git push origin v1.0.0
111
- ```
409
+ - [API Reference](docs/API.md) - Complete API documentation
410
+ - [Architecture](ARCHITECTURE.md) - Detailed architecture documentation
411
+ - [Versioning Guide](docs/VERSIONING.md) - Forge Viewer version management
412
+ - [Collaborative API](docs/COLLABORATIVE-API.md) - Real-time collaboration guide
112
413
 
113
414
  ## License
114
415
 
@@ -118,10 +419,15 @@ MIT
118
419
 
119
420
  **Duong Tran Quang**
120
421
 
121
- - Email: <baymax.contact@gmail.com>
422
+ - Email: baymax.contact@gmail.com
122
423
  - GitHub: [@DTDucas](https://github.com/DTDucas)
123
424
  - LinkedIn: [dtducas](https://www.linkedin.com/in/dtducas)
124
425
 
125
- ## Issues
426
+ ## Support
427
+
428
+ - [Report bugs](https://github.com/DTDucas/wh-forge-viewer/issues)
429
+ - [Request features](https://github.com/DTDucas/wh-forge-viewer/issues)
430
+
431
+ ---
126
432
 
127
- Report bugs and feature requests at [GitHub Issues](https://github.com/DTDucas/wh-forge-viewer/issues)
433
+ Made with care by [DTDucas](https://github.com/DTDucas)