Preview.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import React, {
  2. UIEventHandler, useCallback, useEffect, useMemo, useState,
  3. } from 'react';
  4. import { Subscribe } from 'unstated';
  5. import { withUnstatedContainers } from '../UnstatedUtils';
  6. import RevisionBody from '../Page/RevisionBody';
  7. import AppContainer from '~/client/services/AppContainer';
  8. import EditorContainer from '~/client/services/EditorContainer';
  9. type Props = {
  10. appContainer: AppContainer,
  11. editorContainer: EditorContainer,
  12. markdown?: string,
  13. pagePath?: string,
  14. inputRef?: React.RefObject<HTMLDivElement>,
  15. isMathJaxEnabled?: boolean,
  16. renderMathJaxOnInit?: boolean,
  17. onScroll?: UIEventHandler<HTMLDivElement>,
  18. }
  19. const Preview = (props: Props): JSX.Element => {
  20. const {
  21. appContainer,
  22. markdown, pagePath,
  23. inputRef,
  24. onScroll,
  25. } = props;
  26. const [html, setHtml] = useState('');
  27. const { interceptorManager } = appContainer;
  28. const growiRenderer = props.appContainer.getRenderer('editor');
  29. const context = useMemo(() => {
  30. return {
  31. markdown,
  32. pagePath,
  33. currentPathname: decodeURIComponent(window.location.pathname),
  34. parsedHTML: null,
  35. };
  36. }, [markdown, pagePath]);
  37. const renderPreview = useCallback(async() => {
  38. if (interceptorManager != null) {
  39. await interceptorManager.process('preRenderPreview', context);
  40. await interceptorManager.process('prePreProcess', context);
  41. context.markdown = growiRenderer.preProcess(context.markdown, context);
  42. await interceptorManager.process('postPreProcess', context);
  43. context.parsedHTML = growiRenderer.process(context.markdown, context);
  44. await interceptorManager.process('prePostProcess', context);
  45. context.parsedHTML = growiRenderer.postProcess(context.parsedHTML, context);
  46. await interceptorManager.process('postPostProcess', context);
  47. await interceptorManager.process('preRenderPreviewHtml', context);
  48. }
  49. setHtml(context.parsedHTML ?? '');
  50. }, [interceptorManager, context, growiRenderer]);
  51. useEffect(() => {
  52. if (markdown == null) {
  53. setHtml('');
  54. }
  55. renderPreview();
  56. }, [markdown, renderPreview]);
  57. useEffect(() => {
  58. if (html == null) {
  59. return;
  60. }
  61. if (interceptorManager != null) {
  62. interceptorManager.process('postRenderPreviewHtml', {
  63. ...context,
  64. parsedHTML: html,
  65. });
  66. }
  67. }, [context, html, interceptorManager]);
  68. return (
  69. <Subscribe to={[EditorContainer]}>
  70. { editorContainer => (
  71. <div
  72. className="page-editor-preview-body"
  73. ref={inputRef}
  74. onScroll={onScroll}
  75. >
  76. <RevisionBody
  77. {...props}
  78. html={html}
  79. renderMathJaxInRealtime={editorContainer.state.previewOptions.renderMathJaxInRealtime}
  80. />
  81. </div>
  82. ) }
  83. </Subscribe>
  84. );
  85. };
  86. /**
  87. * Wrapper component for using unstated
  88. */
  89. const PreviewWrapper = withUnstatedContainers(Preview, [AppContainer]);
  90. export default PreviewWrapper;