drawio-interceptor.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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-commons';
  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. // define callback function invoked by viewer.min.js of draw.io
  18. // refs: https://github.com/jgraph/drawio/blob/v12.9.1/etc/build/build.xml#L219-L232
  19. window.onDrawioViewerLoad = function() {
  20. const DrawioViewer = window.GraphViewer;
  21. if (DrawioViewer != null) {
  22. // disable useResizeSensor and checkVisibleState
  23. // for preventing resize event by viewer.min.js
  24. DrawioViewer.useResizeSensor = false;
  25. DrawioViewer.prototype.checkVisibleState = false;
  26. // initialize
  27. DrawioViewer.processElements();
  28. }
  29. };
  30. }
  31. /**
  32. * @inheritdoc
  33. */
  34. isInterceptWhen(contextName) {
  35. return (
  36. contextName === 'preRenderHtml'
  37. || contextName === 'preRenderPreviewHtml'
  38. || contextName === 'postRenderHtml'
  39. || contextName === 'postRenderPreviewHtml'
  40. );
  41. }
  42. /**
  43. * @inheritdoc
  44. */
  45. isProcessableParallel() {
  46. return false;
  47. }
  48. /**
  49. * @inheritdoc
  50. */
  51. async process(contextName, ...args) {
  52. const context = Object.assign(args[0]); // clone
  53. if (contextName === 'preRenderHtml' || contextName === 'preRenderPreviewHtml') {
  54. return this.drawioPreRender(contextName, context);
  55. }
  56. if (contextName === 'postRenderHtml' || contextName === 'postRenderPreviewHtml') {
  57. this.drawioPostRender(contextName, context);
  58. return;
  59. }
  60. }
  61. /**
  62. * @inheritdoc
  63. */
  64. createRandomStr(length) {
  65. const bag = 'abcdefghijklmnopqrstuvwxyz0123456789';
  66. let generated = '';
  67. for (let i = 0; i < length; i++) {
  68. generated += bag[Math.floor(Math.random() * bag.length)];
  69. }
  70. return generated;
  71. }
  72. /**
  73. * @inheritdoc
  74. */
  75. drawioPreRender(contextName, context) {
  76. const div = document.createElement('div');
  77. div.innerHTML = context.parsedHTML;
  78. context.DrawioMap = {};
  79. Array.from(div.querySelectorAll('.mxgraph')).forEach((element) => {
  80. const domId = `mxgraph-${this.createRandomStr(8)}`;
  81. context.DrawioMap[domId] = {
  82. rangeLineNumberOfMarkdown: {
  83. beginLineNumber: element.parentNode.dataset.beginLineNumberOfMarkdown,
  84. endLineNumber: element.parentNode.dataset.endLineNumberOfMarkdown,
  85. },
  86. contentHtml: element.outerHTML,
  87. };
  88. element.outerHTML = `<div id="${domId}"></div>`;
  89. });
  90. context.parsedHTML = div.innerHTML;
  91. // unmount
  92. if (contextName === 'preRenderPreviewHtml') {
  93. this.unmountPreviousReactDOMs(context);
  94. }
  95. // resolve
  96. return context;
  97. }
  98. /**
  99. * @inheritdoc
  100. */
  101. drawioPostRender(contextName, context) {
  102. const isPreview = (contextName === 'postRenderPreviewHtml');
  103. Object.keys(context.DrawioMap).forEach((domId) => {
  104. const elem = document.getElementById(domId);
  105. if (elem) {
  106. this.renderReactDOM(context.DrawioMap[domId], elem, isPreview);
  107. }
  108. });
  109. }
  110. /**
  111. * @inheritdoc
  112. */
  113. renderReactDOM(drawioMapEntry, elem, isPreview) {
  114. ReactDOM.render(
  115. // eslint-disable-next-line react/jsx-filename-extension
  116. <Provider inject={[this.appContainer]}>
  117. <Drawio
  118. drawioContent={drawioMapEntry.contentHtml}
  119. isPreview={isPreview}
  120. rangeLineNumberOfMarkdown={drawioMapEntry.rangeLineNumberOfMarkdown}
  121. />
  122. </Provider>,
  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. }