document-diff-viewer.tsx 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. import { useCallback, useEffect, useRef, useState } from 'react'
  2. import withErrorBoundary from '../../../../infrastructure/error-boundary'
  3. import { ErrorBoundaryFallback } from '../../../../shared/components/error-boundary-fallback'
  4. import { EditorState, Extension } from '@codemirror/state'
  5. import { EditorView, lineNumbers } from '@codemirror/view'
  6. import { indentationMarkers } from '@replit/codemirror-indentation-markers'
  7. import { highlights, setHighlightsEffect } from '../../extensions/highlights'
  8. import useScopeValue from '../../../../shared/hooks/use-scope-value'
  9. import { theme, Options } from '../../extensions/theme'
  10. import { indentUnit } from '@codemirror/language'
  11. import { Highlight } from '../../services/types/doc'
  12. type FontFamily = 'monaco' | 'lucida'
  13. type LineHeight = 'compact' | 'normal' | 'wide'
  14. type OverallTheme = '' | 'light-'
  15. function extensions(themeOptions: Options): Extension[] {
  16. return [
  17. EditorView.editable.of(false),
  18. lineNumbers(),
  19. EditorView.lineWrapping,
  20. indentUnit.of(' '),
  21. indentationMarkers({ hideFirstIndent: true, highlightActiveBlock: false }),
  22. highlights(),
  23. theme(themeOptions),
  24. ]
  25. }
  26. function DocumentDiffViewer({
  27. doc,
  28. highlights,
  29. }: {
  30. doc: string
  31. highlights: Highlight[]
  32. }) {
  33. const [fontFamily] = useScopeValue<FontFamily>('settings.fontFamily')
  34. const [fontSize] = useScopeValue<number>('settings.fontSize')
  35. const [lineHeight] = useScopeValue<LineHeight>('settings.lineHeight')
  36. const [overallTheme] = useScopeValue<OverallTheme>('settings.overallTheme')
  37. const [state] = useState(() => {
  38. return EditorState.create({
  39. doc,
  40. extensions: extensions({
  41. fontFamily,
  42. fontSize,
  43. lineHeight,
  44. overallTheme,
  45. }),
  46. })
  47. })
  48. const view = useRef(
  49. new EditorView({
  50. state,
  51. })
  52. ).current
  53. // Append the editor view DOM to the container node when mounted
  54. const containerRef = useCallback(
  55. node => {
  56. if (node) {
  57. node.appendChild(view.dom)
  58. }
  59. },
  60. [view]
  61. )
  62. useEffect(() => {
  63. view.dispatch({
  64. changes: { from: 0, to: view.state.doc.length, insert: doc },
  65. effects: setHighlightsEffect.of(highlights),
  66. })
  67. }, [doc, highlights, view])
  68. return <div ref={containerRef} className="document-diff-container" />
  69. }
  70. export default withErrorBoundary(DocumentDiffViewer, ErrorBoundaryFallback)