ui-nyla/src/components/ContentPreview/renderers/WordRenderer.tsx

110 lines
3 KiB
TypeScript

import { useEffect, useMemo, useRef, useState } from 'react';
import { renderAsync } from 'docx-preview';
import { useLanguage } from '../../../providers/language/LanguageContext';
import styles from '../ContentPreview.module.css';
interface WordRendererProps {
blob: Blob;
fileName: string;
mimeType?: string;
onError: (message: string) => void;
}
const SUPPORTED_MIME_TYPES = new Set([
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
]);
export function isWordMimeType(mimeType?: string, fileName?: string): boolean {
if (mimeType && SUPPORTED_MIME_TYPES.has(mimeType)) return true;
if (fileName && /\.docx$/i.test(fileName)) return true;
return false;
}
export function WordRenderer({ blob, fileName, mimeType, onError }: WordRendererProps) {
const { t } = useLanguage();
const bodyRef = useRef<HTMLDivElement | null>(null);
const styleRef = useRef<HTMLDivElement | null>(null);
const [loading, setLoading] = useState(true);
const [localError, setLocalError] = useState<string | null>(null);
const isLegacyDoc = useMemo(
() => mimeType === 'application/msword' || /\.doc$/i.test(fileName),
[mimeType, fileName],
);
useEffect(() => {
let cancelled = false;
if (isLegacyDoc) {
const msg = t(
'Das alte Word-Format (.doc) wird nicht unterstützt. Bitte konvertiere die Datei in .docx.',
);
setLocalError(msg);
setLoading(false);
onError(msg);
return;
}
const body = bodyRef.current;
const styleContainer = styleRef.current;
if (!body || !styleContainer) return;
body.innerHTML = '';
styleContainer.innerHTML = '';
setLoading(true);
setLocalError(null);
renderAsync(blob, body, styleContainer, {
className: 'docx-preview',
inWrapper: true,
ignoreWidth: false,
ignoreHeight: false,
ignoreFonts: false,
breakPages: true,
experimental: true,
trimXmlDeclaration: true,
useBase64URL: true,
renderHeaders: true,
renderFooters: true,
renderFootnotes: true,
renderEndnotes: true,
})
.then(() => {
if (cancelled) return;
setLoading(false);
})
.catch(err => {
if (cancelled) return;
const msg =
err?.message ?? t('Word-Dokument konnte nicht gerendert werden.');
setLocalError(msg);
setLoading(false);
onError(msg);
});
return () => {
cancelled = true;
};
}, [blob, isLegacyDoc, onError, t]);
if (localError) {
return (
<div className={styles.textContainer}>
<div className={styles.textHeader}>
<span className={styles.textTitle}>{fileName}</span>
</div>
<div style={{ padding: '1rem', color: 'var(--color-error)' }}>{localError}</div>
</div>
);
}
return (
<div className={styles.docxContainer}>
{loading && (
<div className={styles.docxLoading}>{t('Word-Dokument wird geladen...')}</div>
)}
<div ref={styleRef} />
<div ref={bodyRef} className={styles.docxBody} />
</div>
);
}