drawio-interceptor.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. /* eslint-disable import/prefer-default-export */
  2. import React from 'react';
  3. import ReactDOM from 'react-dom';
  4. import { Provider } from 'unstated';
  5. import { BasicInterceptor } from '@growi/core';
  6. import Drawio from '~/components/Drawio';
  7. /**
  8. * The interceptor for draw.io
  9. *
  10. * replace draw.io tag (render by markdown-it-drawio-viewer) to a React target element
  11. */
  12. export class DrawioInterceptor extends BasicInterceptor {
  13. constructor(appContainer) {
  14. super();
  15. this.previousPreviewContext = null;
  16. this.appContainer = appContainer;
  17. }
  18. /**
  19. * @inheritdoc
  20. */
  21. isInterceptWhen(contextName) {
  22. return (
  23. contextName === 'preRenderHtml'
  24. || contextName === 'preRenderPreviewHtml'
  25. || contextName === 'postRenderHtml'
  26. || contextName === 'postRenderPreviewHtml'
  27. );
  28. }
  29. /**
  30. * @inheritdoc
  31. */
  32. isProcessableParallel() {
  33. return false;
  34. }
  35. /**
  36. * @inheritdoc
  37. */
  38. async process(contextName, ...args) {
  39. const context = Object.assign(args[0]); // clone
  40. if (contextName === 'preRenderHtml' || contextName === 'preRenderPreviewHtml') {
  41. return this.drawioPreRender(contextName, context);
  42. }
  43. if (contextName === 'postRenderHtml' || contextName === 'postRenderPreviewHtml') {
  44. this.drawioPostRender(contextName, context);
  45. return;
  46. }
  47. }
  48. /**
  49. * @inheritdoc
  50. */
  51. createRandomStr(length) {
  52. const bag = 'abcdefghijklmnopqrstuvwxyz0123456789';
  53. let generated = '';
  54. for (let i = 0; i < length; i++) {
  55. generated += bag[Math.floor(Math.random() * bag.length)];
  56. }
  57. return generated;
  58. }
  59. /**
  60. * @inheritdoc
  61. */
  62. drawioPreRender(contextName, context) {
  63. const div = document.createElement('div');
  64. div.innerHTML = context.parsedHTML;
  65. context.DrawioMap = {};
  66. Array.from(div.querySelectorAll('.mxgraph')).forEach((element) => {
  67. const domId = `mxgraph-${this.createRandomStr(8)}`;
  68. context.DrawioMap[domId] = {
  69. rangeLineNumberOfMarkdown: {
  70. beginLineNumber: element.parentNode.dataset.beginLineNumberOfMarkdown,
  71. endLineNumber: element.parentNode.dataset.endLineNumberOfMarkdown,
  72. },
  73. contentHtml: element.outerHTML,
  74. };
  75. element.outerHTML = `<div id="${domId}"></div>`;
  76. });
  77. context.parsedHTML = div.innerHTML;
  78. // unmount
  79. if (contextName === 'preRenderPreviewHtml') {
  80. this.unmountPreviousReactDOMs(context);
  81. }
  82. // resolve
  83. return context;
  84. }
  85. /**
  86. * @inheritdoc
  87. */
  88. drawioPostRender(contextName, context) {
  89. const isPreview = (contextName === 'postRenderPreviewHtml');
  90. const editorContainer = this.appContainer.getContainer('EditorContainer');
  91. const renderDrawioInRealtime = editorContainer.state.previewOptions.renderDrawioInRealtime;
  92. Object.keys(context.DrawioMap).forEach((domId) => {
  93. const elem = document.getElementById(domId);
  94. if (elem) {
  95. if (isPreview && !renderDrawioInRealtime) {
  96. this.renderDisabledDrawioReactDOM(context.DrawioMap[domId], elem, isPreview);
  97. }
  98. else {
  99. this.renderReactDOM(context.DrawioMap[domId], elem, isPreview);
  100. }
  101. }
  102. });
  103. }
  104. /**
  105. * @inheritdoc
  106. */
  107. renderReactDOM(drawioMapEntry, elem, isPreview) {
  108. ReactDOM.render(
  109. // eslint-disable-next-line react/jsx-filename-extension
  110. <Provider inject={[this.appContainer]}>
  111. <Drawio
  112. drawioContent={drawioMapEntry.contentHtml}
  113. isPreview={isPreview}
  114. rangeLineNumberOfMarkdown={drawioMapEntry.rangeLineNumberOfMarkdown}
  115. />
  116. </Provider>,
  117. elem,
  118. );
  119. }
  120. renderDisabledDrawioReactDOM(drawioMapEntry, elem, isPreview) {
  121. ReactDOM.render(
  122. // eslint-disable-next-line react/jsx-filename-extension
  123. <div className="alert alert-light text-dark">Rendering of draw.io is disabled.</div>,
  124. elem,
  125. );
  126. }
  127. /**
  128. * @inheritdoc
  129. */
  130. unmountPreviousReactDOMs(newContext) {
  131. if (this.previousPreviewContext != null) {
  132. Array.from(document.querySelectorAll('.mxgraph')).forEach((element) => {
  133. ReactDOM.unmountComponentAtNode(element);
  134. });
  135. }
  136. this.previousPreviewContext = newContext;
  137. }
  138. }