DrawioModal.jsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import React from 'react';
  2. import i18next from 'i18next';
  3. import PropTypes from 'prop-types';
  4. import {
  5. Modal,
  6. ModalBody,
  7. } from 'reactstrap';
  8. import { getDiagramsNetLangCode } from '~/client/util/locale-utils';
  9. class DrawioModal extends React.PureComponent {
  10. constructor(props) {
  11. super(props);
  12. this.state = {
  13. show: false,
  14. drawioMxFile: '',
  15. };
  16. this.headerColor = '#334455';
  17. this.fontFamily = "Lato, -apple-system, BlinkMacSystemFont, 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif";
  18. this.init = this.init.bind(this);
  19. this.cancel = this.cancel.bind(this);
  20. this.receiveFromDrawio = this.receiveFromDrawio.bind(this);
  21. }
  22. init(drawioMxFile) {
  23. const initDrawioMxFile = drawioMxFile;
  24. this.setState(
  25. {
  26. drawioMxFile: initDrawioMxFile,
  27. },
  28. );
  29. }
  30. show(drawioMxFile) {
  31. this.init(drawioMxFile);
  32. window.addEventListener('message', this.receiveFromDrawio);
  33. this.setState({ show: true });
  34. }
  35. hide() {
  36. this.setState({
  37. show: false,
  38. });
  39. }
  40. cancel() {
  41. this.hide();
  42. }
  43. receiveFromDrawio(event) {
  44. if (event.data === 'ready') {
  45. event.source.postMessage(this.state.drawioMxFile, '*');
  46. return;
  47. }
  48. if (event.data === '{"event":"configure"}') {
  49. if (event.source == null) {
  50. return;
  51. }
  52. // refs:
  53. // * https://desk.draw.io/support/solutions/articles/16000103852-how-to-customise-the-draw-io-interface
  54. // * https://desk.draw.io/support/solutions/articles/16000042544-how-does-embed-mode-work-
  55. // * https://desk.draw.io/support/solutions/articles/16000058316-how-to-configure-draw-io-
  56. event.source.postMessage(JSON.stringify({
  57. action: 'configure',
  58. config: {
  59. css: `
  60. .geMenubarContainer { background-color: ${this.headerColor} !important; }
  61. .geMenubar { background-color: ${this.headerColor} !important; }
  62. .geEditor { font-family: ${this.fontFamily} !important; }
  63. html td.mxPopupMenuItem {
  64. font-family: ${this.fontFamily} !important;
  65. font-size: 8pt !important;
  66. }
  67. `,
  68. customFonts: ['Lato', 'Charter'],
  69. },
  70. }), '*');
  71. return;
  72. }
  73. if (typeof event.data === 'string' && event.data.match(/mxfile/)) {
  74. if (event.data.length > 0) {
  75. const parser = new DOMParser();
  76. const dom = parser.parseFromString(event.data, 'text/xml');
  77. const value = dom.getElementsByTagName('diagram')[0].innerHTML;
  78. if (this.props.onSave != null) {
  79. this.props.onSave(value);
  80. }
  81. }
  82. window.removeEventListener('message', this.receiveFromDrawio);
  83. this.hide();
  84. return;
  85. }
  86. if (typeof event.data === 'string' && event.data.length === 0) {
  87. window.removeEventListener('message', this.receiveFromDrawio);
  88. this.hide();
  89. return;
  90. }
  91. // NOTHING DONE. (Receive unknown iframe message.)
  92. }
  93. get drawioURL() {
  94. const { config } = this.props.appContainer;
  95. const drawioUri = config.env.DRAWIO_URI || 'https://embed.diagrams.net/';
  96. const url = new URL(drawioUri);
  97. // refs: https://desk.draw.io/support/solutions/articles/16000042546-what-url-parameters-are-supported-
  98. url.searchParams.append('spin', 1);
  99. url.searchParams.append('embed', 1);
  100. url.searchParams.append('lang', getDiagramsNetLangCode(i18next.language));
  101. url.searchParams.append('ui', 'atlas');
  102. url.searchParams.append('configure', 1);
  103. return url;
  104. }
  105. render() {
  106. return (
  107. <Modal
  108. isOpen={this.state.show}
  109. toggle={this.cancel}
  110. backdrop="static"
  111. className="drawio-modal grw-body-only-modal-expanded"
  112. size="xl"
  113. keyboard={false}
  114. >
  115. <ModalBody className="p-0">
  116. {/* Loading spinner */}
  117. <div className="w-100 h-100 position-absolute d-flex">
  118. <div className="mx-auto my-auto">
  119. <i className="fa fa-3x fa-spinner fa-pulse mx-auto text-muted"></i>
  120. </div>
  121. </div>
  122. {/* iframe */}
  123. <div className="w-100 h-100 position-absolute d-flex">
  124. { this.state.show && (
  125. <iframe
  126. src={this.drawioURL}
  127. className="border-0 flex-grow-1"
  128. >
  129. </iframe>
  130. ) }
  131. </div>
  132. </ModalBody>
  133. </Modal>
  134. );
  135. }
  136. }
  137. DrawioModal.propTypes = {
  138. onSave: PropTypes.func,
  139. };
  140. export default DrawioModal;