dpu-onlyoffice-react
Version:
React component for OnlyOffice Document Server integration with version history support
477 lines (402 loc) โข 12 kB
Markdown
A React component library for integrating OnlyOffice Document Server with React and Next.js applications. This package provides a clean, TypeScript-first API for viewing and editing Office documents with full version history support.
- ๐ **Document Viewing & Editing** - Support for Word, Excel, and PowerPoint documents
- ๐ **Version History** - Complete version management with restore capabilities
- โก **React 18+ & Next.js** - Optimized for modern React applications
- ๐ฏ **TypeScript** - Full TypeScript support with comprehensive type definitions
- ๐ ๏ธ **Customizable** - Flexible configuration options and event handling
- ๐งน **Memory Management** - Proper cleanup and instance management
- ๐ฆ **Tree Shaking** - Optimized bundle size with ES modules
```bash
npm install dpu-onlyoffice-react
yarn add dpu-onlyoffice-react
pnpm add dpu-onlyoffice-react
```
```tsx
import React from 'react';
import { DocumentEditor, OnlyOfficeConfig } from 'dpu-onlyoffice-react';
const MyComponent = () => {
const config: OnlyOfficeConfig = {
document: {
fileType: 'docx',
key: 'unique-document-key',
title: 'My Document',
url: 'https://example.com/document.docx',
permissions: {
edit: true,
download: true,
print: true
}
},
documentType: 'word',
editorConfig: {
mode: 'edit',
lang: 'en',
user: {
id: 'user-123',
name: 'John Doe'
},
callbackUrl: 'https://your-api.com/callback'
},
height: '100%',
width: '100%'
};
return (
<div style={{ height: '100vh' }}>
<DocumentEditor
config={config}
editorKey="my-editor"
onError={(error) => console.error('Editor error:', error)}
/>
</div>
);
};
```
```tsx
import React from 'react';
import { DocumentEditor, OnlyOfficeConfig, DocumentEventHandlers, refreshHistory, setHistoryData } from 'dpu-onlyoffice-react';
const MyComponent = ({ fileId, documentUrl }) => {
// Your API functions
const fetchVersionHistory = async (fileId) => {
const response = await fetch(`/api/files/${fileId}/history`);
return response.json();
};
const fetchHistoryData = async (fileId, version) => {
const response = await fetch(`/api/files/${fileId}/history/${version}`);
return response.json();
};
// Event handlers - you call API, library handles OnlyOffice
const eventHandlers: DocumentEventHandlers = {
onRequestHistory: async (event) => {
try {
const history = await fetchVersionHistory(fileId);
refreshHistory(history, 'History loaded', fileId);
} catch (error) {
refreshHistory({ error: 'Failed to load history' }, 'Error', fileId);
}
},
onRequestHistoryData: async (event) => {
try {
const data = await fetchHistoryData(fileId, event.data);
setHistoryData(data, fileId);
} catch (error) {
setHistoryData({ error: error.message }, fileId);
}
}
};
const config: OnlyOfficeConfig = {
document: {
fileType: 'docx',
key: fileId,
title: 'My Document',
url: documentUrl
},
documentType: 'word',
editorConfig: {
mode: 'edit',
user: { id: 'user-123', name: 'John Doe' }
}
};
return (
<div style={{ height: '100vh' }}>
<DocumentEditor
config={config}
editorKey={fileId}
eventHandlers={eventHandlers}
onError={(error) => console.error('Editor error:', error)}
/>
</div>
);
};
```
```tsx
import React from 'react';
import { DocumentEditor, useOnlyOfficeEditor, OnlyOfficeConfig } from 'dpu-onlyoffice-react';
const DocumentWithHistory = ({ fileId }: { fileId: string }) => {
const { createEventHandlers } = useOnlyOfficeEditor();
const config: OnlyOfficeConfig = {
document: {
fileType: 'docx',
key: fileId,
title: 'Document with History',
url: `https://api.example.com/files/${fileId}/download`
},
documentType: 'word',
editorConfig: {
mode: 'edit',
user: {
id: 'user-123',
name: 'John Doe'
}
},
events: createEventHandlers({
fileId,
onHistoryRequest: async (fileId) => {
const response = await fetch(`/api/files/${fileId}/history`);
return response.json();
},
onHistoryDataRequest: async (fileId, version) => {
const response = await fetch(`/api/files/${fileId}/history/${version}`);
return response.json();
},
onRestore: async (fileId, version) => {
const response = await fetch(`/api/files/${fileId}/restore`, {
method: 'POST',
body: JSON.stringify({ version })
});
return response.json();
}
})
};
return (
<div style={{ height: '100vh' }}>
<DocumentEditor config={config} editorKey={fileId} />
</div>
);
};
```
```tsx
import React, { useEffect } from 'react';
import { loadOnlyOfficeScript, DocumentEditor } from '@dpu/onlyoffice-react';
const App = () => {
useEffect(() => {
// Load OnlyOffice script with custom configuration
loadOnlyOfficeScript({
scriptUrl: 'https://your-onlyoffice-server.com/web-apps/apps/api/documents/api.js',
fallbackUrl: 'https://backup-server.com/web-apps/apps/api/documents/api.js',
timeout: 30000
}).catch(console.error);
}, []);
return (
<DocumentEditor
config={config}
editorKey="custom-editor"
/>
);
};
```
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `config` | `OnlyOfficeConfig` | โ
| OnlyOffice configuration object |
| `editorKey` | `string \| number` | โ | Unique key for editor instance (default: 1) |
| `className` | `string` | โ | CSS class name for container |
| `style` | `React.CSSProperties` | โ | Inline styles for container |
| `onError` | `(error: Error) => void` | โ | Error callback function |
```typescript
interface OnlyOfficeConfig {
document: {
fileType: string;
key: string;
title: string;
url: string;
permissions?: {
edit?: boolean;
download?: boolean;
print?: boolean;
review?: boolean;
};
};
documentType: 'word' | 'cell' | 'slide';
editorConfig: {
mode?: 'edit' | 'view';
lang?: string;
location?: string;
customization?: {
uiTheme?: string;
logo?: {
image?: string;
url?: string;
};
about?: {
visible?: boolean;
text?: string;
};
feedback?: {
visible?: boolean;
url?: string;
};
};
user?: {
id: string;
name: string;
group?: string;
};
callbackUrl?: string;
coEditing?: {
mode?: 'fast' | 'strict';
change?: boolean;
};
};
height?: string | number;
width?: string | number;
events?: OnlyOfficeEvents;
}
```
```typescript
const {
isLoading,
error,
setError,
getEditorInstance,
setEditorInstance,
refreshHistory,
setHistoryData,
requestClose,
destroyEditor,
createEventHandlers
} = useOnlyOfficeEditor();
```
```typescript
// Load OnlyOffice script
loadOnlyOfficeScript(options?: OnlyOfficeScriptOptions): Promise<void>
// Check if script is loaded
isOnlyOfficeScriptLoaded(): boolean
// Reset script state (for testing)
resetScriptState(): void
// Get current script state
getScriptState(): { loaded: boolean; loading: boolean; hasPromise: boolean }
```
1. Install OnlyOffice Document Server
2. Configure your server URL in the script loader
3. Set up proper CORS headers
4. Configure callback URL for document saving
```bash
NEXT_PUBLIC_ONLYOFFICE_SERVER_URL=https://your-onlyoffice-server.com
NEXT_PUBLIC_ONLYOFFICE_CALLBACK_URL=https://your-app.com/api/onlyoffice/callback
```
```tsx
// pages/document/[id].tsx
import { GetServerSideProps } from 'next';
import { DocumentEditor, OnlyOfficeConfig } from 'dpu-onlyoffice-react';
interface DocumentPageProps {
document: {
id: string;
title: string;
url: string;
fileType: string;
};
}
export default function DocumentPage({ document }: DocumentPageProps) {
const config: OnlyOfficeConfig = {
document: {
fileType: document.fileType,
key: document.id,
title: document.title,
url: document.url
},
documentType: document.fileType.includes('word') ? 'word' :
document.fileType.includes('excel') ? 'cell' : 'slide',
editorConfig: {
mode: 'edit',
user: {
id: 'current-user-id',
name: 'Current User'
},
callbackUrl: `${process.env.NEXT_PUBLIC_ONLYOFFICE_CALLBACK_URL}?documentId=${document.id}`
}
};
return (
<div style={{ height: '100vh' }}>
<DocumentEditor config={config} editorKey={document.id} />
</div>
);
}
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
// Fetch document data from your API
const document = await fetchDocument(params?.id as string);
return {
props: {
document
}
};
};
```
```tsx
import React, { useState } from 'react';
import { DocumentEditor, OnlyOfficeConfig } from 'dpu-onlyoffice-react';
const DocumentWithErrorHandling = () => {
const [error, setError] = useState<Error | null>(null);
const config: OnlyOfficeConfig = {
// ... your config
};
if (error) {
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h3>Error loading document</h3>
<p>{error.message}</p>
<button onClick={() => setError(null)}>Retry</button>
</div>
);
}
return (
<DocumentEditor
config={config}
onError={setError}
/>
);
};
```
1. **Script Loading Errors**
- Check OnlyOffice server URL
- Verify CORS configuration
- Check network connectivity
2. **Document Not Loading**
- Verify document URL is accessible
- Check document key uniqueness
- Ensure proper file permissions
3. **Version History Issues**
- Implement proper API endpoints
- Check data format compatibility
- Verify user permissions
Enable debug logging by setting the log level:
```typescript
// In your app initialization
if (process.env.NODE_ENV === 'development') {
console.log('OnlyOffice Debug Mode Enabled');
}
```
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Support
For support and questions:
- Create an issue on GitHub
- Check the [OnlyOffice Documentation](https://api.onlyoffice.com/)
- Review the [React Documentation](https://reactjs.org/docs/)
## Changelog
### 1.0.0
- Initial release
- React 18+ support
- TypeScript definitions
- Version history support
- Memory management
- Custom script loading