drawio-interceptor.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. /* eslint-disable import/prefer-default-export */
  2. import React from 'react';
  3. import { BasicInterceptor } from '@growi/core';
  4. import ReactDOM from 'react-dom';
  5. import { Provider } from 'unstated';
  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 renderDrawioInRealtime = context.editorSettings?.renderDrawioInRealtime;
  91. Object.keys(context.DrawioMap).forEach((domId) => {
  92. const elem = document.getElementById(domId);
  93. if (elem) {
  94. if (isPreview && !renderDrawioInRealtime) {
  95. this.renderDisabledDrawioReactDOM(context.DrawioMap[domId], elem, isPreview);
  96. }
  97. else {
  98. this.renderReactDOM(context.DrawioMap[domId], elem, isPreview);
  99. }
  100. }
  101. });
  102. }
  103. /**
  104. * @inheritdoc
  105. */
  106. renderReactDOM(drawioMapEntry, elem, isPreview) {
  107. ReactDOM.render(
  108. // eslint-disable-next-line react/jsx-filename-extension
  109. <Provider inject={[this.appContainer]}>
  110. <Drawio
  111. drawioContent={drawioMapEntry.contentHtml}
  112. isPreview={isPreview}
  113. rangeLineNumberOfMarkdown={drawioMapEntry.rangeLineNumberOfMarkdown}
  114. />
  115. </Provider>,
  116. elem,
  117. );
  118. }
  119. renderDisabledDrawioReactDOM(drawioMapEntry, elem, isPreview) {
  120. ReactDOM.render(
  121. // eslint-disable-next-line react/jsx-filename-extension
  122. <div className="alert alert-light text-dark">Rendering of draw.io is disabled.</div>,
  123. elem,
  124. );
  125. }
  126. /**
  127. * @inheritdoc
  128. */
  129. unmountPreviousReactDOMs(newContext) {
  130. if (this.previousPreviewContext != null) {
  131. Array.from(document.querySelectorAll('.mxgraph')).forEach((element) => {
  132. ReactDOM.unmountComponentAtNode(element);
  133. });
  134. }
  135. this.previousPreviewContext = newContext;
  136. }
  137. }