drawio-interceptor.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  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. }
  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. Object.keys(context.DrawioMap).forEach((domId) => {
  91. const elem = document.getElementById(domId);
  92. if (elem) {
  93. this.renderReactDOM(context.DrawioMap[domId], elem, isPreview);
  94. }
  95. });
  96. }
  97. /**
  98. * @inheritdoc
  99. */
  100. renderReactDOM(drawioMapEntry, elem, isPreview) {
  101. ReactDOM.render(
  102. // eslint-disable-next-line react/jsx-filename-extension
  103. <Provider inject={[this.appContainer]}>
  104. <Drawio
  105. drawioContent={drawioMapEntry.contentHtml}
  106. isPreview={isPreview}
  107. rangeLineNumberOfMarkdown={drawioMapEntry.rangeLineNumberOfMarkdown}
  108. />
  109. </Provider>,
  110. elem,
  111. );
  112. }
  113. /**
  114. * @inheritdoc
  115. */
  116. unmountPreviousReactDOMs(newContext) {
  117. if (this.previousPreviewContext != null) {
  118. Array.from(document.querySelectorAll('.mxgraph')).forEach((element) => {
  119. ReactDOM.unmountComponentAtNode(element);
  120. });
  121. }
  122. this.previousPreviewContext = newContext;
  123. }
  124. }