@dtducas/wh-forge-viewer 1.0.8-beta → 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 +321 -242
- package/dist/index.d.ts +1038 -2
- package/dist/index.esm.js +49616 -1566
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +49668 -1562
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -1,331 +1,415 @@
|
|
|
1
1
|
# wh-forge-viewer
|
|
2
2
|
|
|
3
|
-
A professional Autodesk Forge Viewer component for React with advanced PDF support,
|
|
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
|
-
### Core
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
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
|
|
27
36
|
|
|
28
37
|
## Installation
|
|
29
38
|
|
|
30
39
|
```bash
|
|
31
|
-
npm install wh-forge-viewer
|
|
40
|
+
npm install @dtducas/wh-forge-viewer
|
|
32
41
|
```
|
|
33
42
|
|
|
34
|
-
##
|
|
43
|
+
## Quick Start
|
|
35
44
|
|
|
36
|
-
|
|
37
|
-
import { ViewerForgePDF } from 'wh-forge-viewer';
|
|
45
|
+
### Basic Viewer
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
```jsx
|
|
48
|
+
import { ViewerForgePDF } from '@dtducas/wh-forge-viewer';
|
|
41
49
|
|
|
50
|
+
function App() {
|
|
42
51
|
return (
|
|
43
52
|
<ViewerForgePDF
|
|
44
53
|
filePath='https://example.com/document.pdf'
|
|
45
54
|
fileExt='pdf'
|
|
46
|
-
|
|
55
|
+
onViewerReady={(viewer) => console.log('Ready:', viewer)}
|
|
47
56
|
/>
|
|
48
57
|
);
|
|
49
58
|
}
|
|
50
59
|
```
|
|
51
60
|
|
|
52
|
-
|
|
61
|
+
### With Markup Tools
|
|
53
62
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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({});
|
|
59
78
|
|
|
60
|
-
|
|
79
|
+
const eventBus = EventBus.getInstance();
|
|
61
80
|
|
|
62
|
-
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (!viewer) return;
|
|
63
83
|
|
|
64
|
-
|
|
84
|
+
const manager = new DrawingManager(viewer, eventBus, {
|
|
85
|
+
autoEnterEditMode: true,
|
|
86
|
+
defaultTool: 'arrow',
|
|
87
|
+
});
|
|
65
88
|
|
|
66
|
-
|
|
67
|
-
- Auto-activates on viewer load
|
|
68
|
-
- Synced with Forge Viewer navigation state
|
|
69
|
-
|
|
70
|
-
- **Document Browser** - Toggle document tree/thumbnail view
|
|
71
|
-
- Auto-opens for all documents (single/multi-page)
|
|
72
|
-
- Positioned on right side with custom styling
|
|
73
|
-
- Default tab: Thumbnails view
|
|
74
|
-
- Adaptive panel height based on content
|
|
75
|
-
- State persists across page navigation
|
|
76
|
-
|
|
77
|
-
- **Download** - Download the current document
|
|
78
|
-
- One-click file download
|
|
79
|
-
- Preserves original filename
|
|
89
|
+
setDrawingManager(manager);
|
|
80
90
|
|
|
81
|
-
|
|
91
|
+
return () => manager.destroy();
|
|
92
|
+
}, [viewer]);
|
|
82
93
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
94
|
+
const handleToolChange = (tool) => {
|
|
95
|
+
drawingManager?.setActiveTool(tool);
|
|
96
|
+
setActiveTool(tool);
|
|
97
|
+
setPropertyConfig(drawingManager?.getCurrentToolPropertyConfig());
|
|
98
|
+
setPropertyValues(drawingManager?.getCurrentToolPropertyValues() || {});
|
|
99
|
+
};
|
|
86
100
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
+
```
|
|
91
132
|
|
|
92
|
-
|
|
133
|
+
### Real-Time Collaboration
|
|
93
134
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
135
|
+
```jsx
|
|
136
|
+
import {
|
|
137
|
+
DrawingManager,
|
|
138
|
+
EventBus,
|
|
139
|
+
CursorOverlay,
|
|
140
|
+
} from '@dtducas/wh-forge-viewer';
|
|
97
141
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
142
|
+
function CollaborativeViewer({ socket }) {
|
|
143
|
+
const [drawingManager, setDrawingManager] = useState(null);
|
|
144
|
+
const [remoteCursors, setRemoteCursors] = useState(new Map());
|
|
101
145
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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]);
|
|
105
175
|
|
|
106
|
-
|
|
176
|
+
return (
|
|
177
|
+
<>
|
|
178
|
+
{/* ... viewer components ... */}
|
|
179
|
+
<CursorOverlay cursors={remoteCursors} />
|
|
180
|
+
</>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
```
|
|
107
184
|
|
|
108
|
-
###
|
|
185
|
+
### Persistence
|
|
109
186
|
|
|
110
|
-
|
|
187
|
+
```javascript
|
|
188
|
+
// Save to localStorage
|
|
189
|
+
const data = drawingManager.getMarkupData();
|
|
190
|
+
localStorage.setItem('markups', JSON.stringify(data));
|
|
111
191
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
- ✅ Tested and verified functionality
|
|
116
|
-
- ✅ Easy to upgrade when ready
|
|
192
|
+
// Load from localStorage
|
|
193
|
+
const saved = JSON.parse(localStorage.getItem('markups'));
|
|
194
|
+
await drawingManager.loadMarkupData(saved, { mode: 'replace' });
|
|
117
195
|
|
|
118
|
-
|
|
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);
|
|
119
199
|
|
|
120
|
-
|
|
200
|
+
// Load from database
|
|
201
|
+
const doc = await db.collection('markups').doc('page-guid-123').get();
|
|
202
|
+
await drawingManager.loadMarkupDataFromJson(doc.data());
|
|
203
|
+
```
|
|
121
204
|
|
|
122
|
-
|
|
123
|
-
- Forge Viewer unloads extensions when navigating between pages
|
|
124
|
-
- This package handles this gracefully with GLOBAL_STATE persistence
|
|
125
|
-
- All states (viewables, page index, panel state) are preserved
|
|
205
|
+
### PDF Export with Markup
|
|
126
206
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
+
}
|
|
132
214
|
|
|
133
|
-
|
|
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
|
+
```
|
|
134
224
|
|
|
135
|
-
|
|
225
|
+
## Components
|
|
136
226
|
|
|
137
|
-
###
|
|
227
|
+
### ViewerForgePDF
|
|
138
228
|
|
|
139
|
-
|
|
140
|
-
- `PaginationManager` - Handles page navigation, viewables management, and smart Document Browser API integration
|
|
141
|
-
- `ToolbarManager` - Creates and manages custom toolbar buttons and their states
|
|
142
|
-
- `DocumentBrowserManager` - Controls Document Browser panel visibility, styling, and state persistence
|
|
229
|
+
Main viewer component.
|
|
143
230
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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 |
|
|
148
238
|
|
|
149
|
-
|
|
150
|
-
- `ToolbarExtension` - Main extension that orchestrates all managers and services
|
|
239
|
+
### MarkupToolbar
|
|
151
240
|
|
|
152
|
-
|
|
241
|
+
Floating toolbar for tool selection.
|
|
153
242
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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 |
|
|
158
249
|
|
|
159
|
-
|
|
160
|
-
- Detects if Document Browser panel is open
|
|
161
|
-
- Uses `_changeModelFn` API when open (no panel refresh)
|
|
162
|
-
- Falls back to `loadDocumentNode` when closed (with state restoration)
|
|
163
|
-
- Automatic thumbnail selection and scroll synchronization
|
|
250
|
+
### PropertyPanel
|
|
164
251
|
|
|
165
|
-
|
|
166
|
-
- Polls every 8ms to detect missing toolbar groups
|
|
167
|
-
- Automatically recreates toolbar if removed by Forge Viewer
|
|
168
|
-
- Syncs button states with actual panel visibility
|
|
169
|
-
- Ensures UI consistency at all times
|
|
252
|
+
Side panel for editing properties.
|
|
170
253
|
|
|
171
|
-
|
|
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 |
|
|
172
260
|
|
|
173
|
-
|
|
261
|
+
### CursorOverlay
|
|
174
262
|
|
|
175
|
-
|
|
263
|
+
Overlay for displaying remote cursors.
|
|
176
264
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
265
|
+
| Prop | Type | Description |
|
|
266
|
+
| -------------- | ------- | ----------------------------- |
|
|
267
|
+
| `cursors` | Map | Remote user cursors |
|
|
268
|
+
| `viewerBounds` | DOMRect | Viewer bounds for positioning |
|
|
180
269
|
|
|
181
|
-
|
|
182
|
-
if (viewer) {
|
|
183
|
-
// Access viewer API
|
|
184
|
-
console.log('Viewer loaded:', viewer);
|
|
185
|
-
|
|
186
|
-
// Listen to Forge Viewer events
|
|
187
|
-
viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, () => {
|
|
188
|
-
console.log('Geometry loaded');
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
}, [viewer]);
|
|
270
|
+
## DrawingManager API
|
|
192
271
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
);
|
|
200
|
-
}
|
|
272
|
+
### Edit Mode
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
await drawingManager.enterEditMode(); // Enter markup mode
|
|
276
|
+
drawingManager.leaveEditMode(); // Exit markup mode
|
|
277
|
+
drawingManager.isEditMode(); // Check if in edit mode
|
|
201
278
|
```
|
|
202
279
|
|
|
203
|
-
###
|
|
280
|
+
### Tools
|
|
204
281
|
|
|
205
|
-
|
|
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
|
+
```
|
|
206
288
|
|
|
207
|
-
|
|
208
|
-
|
|
289
|
+
**Available Tools:**
|
|
290
|
+
`select`, `arrow`, `rectangle`, `ellipse`, `cloud`, `label`, `freehand`, `line`, `polyline`, `polycloud`, `callout`, `image`
|
|
209
291
|
|
|
210
|
-
|
|
292
|
+
### Markup Operations
|
|
211
293
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
+
```
|
|
218
302
|
|
|
219
|
-
|
|
220
|
-
eventBus.on('docBrowser:opened', () => {
|
|
221
|
-
console.log('Document Browser opened');
|
|
222
|
-
});
|
|
303
|
+
### Collaborative API
|
|
223
304
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
```
|
|
305
|
+
```typescript
|
|
306
|
+
// Get markup data for persistence
|
|
307
|
+
const data = drawingManager.getMarkupData(viewableGUID);
|
|
228
308
|
|
|
229
|
-
|
|
309
|
+
// Load markup data
|
|
310
|
+
await drawingManager.loadMarkupData(data, { mode: 'replace' });
|
|
230
311
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
- 💾 Memory efficient
|
|
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
|
+
});
|
|
237
317
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
- Minimal re-renders and DOM updates
|
|
241
|
-
- Efficient event-driven architecture
|
|
242
|
-
- Debounced toolbar healing checks
|
|
318
|
+
// Apply remote changes
|
|
319
|
+
drawingManager.applyDelta(delta);
|
|
243
320
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
- Multi-page: Optimized for scrolling (max 80% viewport)
|
|
321
|
+
// Batch apply (for initial load)
|
|
322
|
+
drawingManager.applyBatch(elements, true);
|
|
323
|
+
```
|
|
248
324
|
|
|
249
|
-
|
|
325
|
+
### User Presence
|
|
250
326
|
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
|
|
327
|
+
```typescript
|
|
328
|
+
// Broadcast local cursor
|
|
329
|
+
const unsub = drawingManager.onLocalCursorMove((position) => {
|
|
330
|
+
socket.emit('cursor:move', { userId, position });
|
|
331
|
+
});
|
|
254
332
|
|
|
255
|
-
|
|
256
|
-
|
|
333
|
+
// Update remote cursor
|
|
334
|
+
drawingManager.updateUserCursor({ userId, userName, color, position });
|
|
257
335
|
|
|
258
|
-
|
|
259
|
-
|
|
336
|
+
// Remove cursor when user leaves
|
|
337
|
+
drawingManager.removeUserCursor(userId);
|
|
338
|
+
|
|
339
|
+
// Get all remote cursors
|
|
340
|
+
const cursors = drawingManager.getRemoteCursors();
|
|
260
341
|
```
|
|
261
342
|
|
|
262
|
-
##
|
|
343
|
+
## EventBus Events
|
|
263
344
|
|
|
264
|
-
|
|
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 |
|
|
265
353
|
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
+
});
|
|
270
362
|
```
|
|
271
363
|
|
|
272
|
-
##
|
|
364
|
+
## Custom Toolbar
|
|
273
365
|
|
|
274
|
-
|
|
366
|
+
Built-in toolbar includes:
|
|
275
367
|
|
|
276
|
-
|
|
277
|
-
# Update version in package.json
|
|
278
|
-
npm version patch # or minor, major
|
|
368
|
+
**Tools Group:**
|
|
279
369
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
370
|
+
- Pan Tool - Navigate the document
|
|
371
|
+
- Document Browser - Toggle thumbnail/tree view
|
|
372
|
+
- Download - Download current document
|
|
283
373
|
|
|
284
|
-
|
|
374
|
+
**Pagination Group:**
|
|
285
375
|
|
|
286
|
-
|
|
376
|
+
- Previous/Next Page navigation
|
|
377
|
+
- Current page indicator
|
|
287
378
|
|
|
288
|
-
**
|
|
379
|
+
**Smart Features:**
|
|
289
380
|
|
|
290
|
-
|
|
381
|
+
- No-refresh navigation when Document Browser is open
|
|
382
|
+
- Self-healing mechanism for toolbar persistence
|
|
383
|
+
- Synchronized button states
|
|
291
384
|
|
|
292
|
-
|
|
293
|
-
- Disable browser extensions during development, OR
|
|
294
|
-
- Add error boundary to catch extension errors
|
|
385
|
+
## Requirements
|
|
295
386
|
|
|
296
|
-
|
|
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+)
|
|
297
390
|
|
|
298
|
-
|
|
299
|
-
1. Check browser console for errors
|
|
300
|
-
2. Verify Autodesk Forge Viewer version (should be v7.108.0)
|
|
301
|
-
3. Try refreshing the page
|
|
302
|
-
4. The self-healing mechanism should auto-fix within seconds
|
|
391
|
+
## Development
|
|
303
392
|
|
|
304
|
-
|
|
393
|
+
```bash
|
|
394
|
+
# Install dependencies
|
|
395
|
+
npm install
|
|
396
|
+
|
|
397
|
+
# Build package
|
|
398
|
+
npm run build
|
|
305
399
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
2. Should auto-correct within 500ms
|
|
309
|
-
3. Check if toolbar group is being removed by other extensions
|
|
400
|
+
# Watch mode
|
|
401
|
+
npm run dev
|
|
310
402
|
|
|
311
|
-
|
|
403
|
+
# Test app
|
|
404
|
+
cd test/app && npm install && npm run dev
|
|
405
|
+
```
|
|
312
406
|
|
|
313
|
-
|
|
314
|
-
- ✨ Smart navigation: Document Browser panel no longer refreshes when using pagination buttons
|
|
315
|
-
- ✨ Auto-scroll thumbnails to center selected page
|
|
316
|
-
- ✨ Adaptive panel height for single vs multi-page documents
|
|
317
|
-
- ✨ Global state persistence across extension reload cycles
|
|
318
|
-
- 🐛 Fixed: Document Browser button state sync after thumbnail navigation
|
|
319
|
-
- 🐛 Fixed: Thumbnail selection not updating when using pagination buttons
|
|
320
|
-
- 🧹 Removed all debug console logs for production
|
|
321
|
-
- 📝 Full code cleanup and optimization
|
|
407
|
+
## Documentation
|
|
322
408
|
|
|
323
|
-
|
|
324
|
-
-
|
|
325
|
-
-
|
|
326
|
-
-
|
|
327
|
-
- Self-healing toolbar mechanism
|
|
328
|
-
- SOLID architecture refactor
|
|
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
|
|
329
413
|
|
|
330
414
|
## License
|
|
331
415
|
|
|
@@ -335,20 +419,15 @@ MIT
|
|
|
335
419
|
|
|
336
420
|
**Duong Tran Quang**
|
|
337
421
|
|
|
338
|
-
- Email:
|
|
422
|
+
- Email: baymax.contact@gmail.com
|
|
339
423
|
- GitHub: [@DTDucas](https://github.com/DTDucas)
|
|
340
424
|
- LinkedIn: [dtducas](https://www.linkedin.com/in/dtducas)
|
|
341
425
|
|
|
342
|
-
## Support
|
|
343
|
-
|
|
344
|
-
- 🐛 [Report bugs](https://github.com/DTDucas/wh-forge-viewer/issues)
|
|
345
|
-
- 💡 [Request features](https://github.com/DTDucas/wh-forge-viewer/issues)
|
|
346
|
-
- 📖 [View documentation](https://github.com/DTDucas/wh-forge-viewer#readme)
|
|
347
|
-
|
|
348
|
-
## Contributing
|
|
426
|
+
## Support
|
|
349
427
|
|
|
350
|
-
|
|
428
|
+
- [Report bugs](https://github.com/DTDucas/wh-forge-viewer/issues)
|
|
429
|
+
- [Request features](https://github.com/DTDucas/wh-forge-viewer/issues)
|
|
351
430
|
|
|
352
431
|
---
|
|
353
432
|
|
|
354
|
-
Made with
|
|
433
|
+
Made with care by [DTDucas](https://github.com/DTDucas)
|