DrawioModal.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import React, {
  2. useCallback,
  3. useEffect,
  4. useMemo,
  5. } from 'react';
  6. import {
  7. Modal,
  8. ModalBody,
  9. } from 'reactstrap';
  10. import { getDiagramsNetLangCode } from '~/client/util/locale-utils';
  11. import { useDrawioUri } from '~/stores/context';
  12. import { useDrawioModal } from '~/stores/modal';
  13. import { usePersonalSettings } from '~/stores/personal-settings';
  14. import loggerFactory from '~/utils/logger';
  15. import { type DrawioConfig, DrawioCommunicationHelper } from './DrawioCommunicationHelper';
  16. const logger = loggerFactory('growi:components:DrawioModal');
  17. const headerColor = '#334455';
  18. const fontFamily = "Lato, -apple-system, BlinkMacSystemFont, 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif";
  19. const drawioConfig: DrawioConfig = {
  20. css: `
  21. .geMenubarContainer { background-color: ${headerColor} !important; }
  22. .geMenubar { background-color: ${headerColor} !important; }
  23. .geEditor { font-family: ${fontFamily} !important; }
  24. html td.mxPopupMenuItem {
  25. font-family: ${fontFamily} !important;
  26. font-size: 8pt !important;
  27. }
  28. `,
  29. customFonts: ['Lato', 'Charter'],
  30. compressXml: true,
  31. };
  32. export const DrawioModal = (): JSX.Element => {
  33. const { data: drawioUri } = useDrawioUri();
  34. const { data: personalSettingsInfo } = usePersonalSettings({
  35. // make immutable
  36. revalidateIfStale: false,
  37. revalidateOnFocus: false,
  38. revalidateOnReconnect: false,
  39. });
  40. const { data: drawioModalData, close: closeDrawioModal } = useDrawioModal();
  41. const isOpened = drawioModalData?.isOpened ?? false;
  42. const drawioUriWithParams = useMemo(() => {
  43. if (drawioUri == null) {
  44. return undefined;
  45. }
  46. let url;
  47. try {
  48. url = new URL(drawioUri);
  49. }
  50. catch (err) {
  51. logger.debug(err);
  52. return undefined;
  53. }
  54. // refs: https://desk.draw.io/support/solutions/articles/16000042546-what-url-parameters-are-supported-
  55. url.searchParams.append('spin', '1');
  56. url.searchParams.append('embed', '1');
  57. url.searchParams.append('lang', getDiagramsNetLangCode(personalSettingsInfo?.lang || 'en'));
  58. url.searchParams.append('ui', 'atlas');
  59. url.searchParams.append('configure', '1');
  60. return url;
  61. }, [drawioUri, personalSettingsInfo?.lang]);
  62. const drawioCommunicationHelper = useMemo(() => {
  63. if (drawioUri == null) {
  64. return undefined;
  65. }
  66. return new DrawioCommunicationHelper(
  67. drawioUri,
  68. drawioConfig,
  69. { onClose: closeDrawioModal, onSave: drawioModalData?.onSave },
  70. );
  71. }, [closeDrawioModal, drawioModalData?.onSave, drawioUri]);
  72. const receiveMessageHandler = useCallback((event: MessageEvent) => {
  73. if (drawioModalData == null) {
  74. return;
  75. }
  76. drawioCommunicationHelper?.onReceiveMessage(event, drawioModalData.drawioMxFile);
  77. }, [drawioCommunicationHelper, drawioModalData]);
  78. useEffect(() => {
  79. if (isOpened) {
  80. window.addEventListener('message', receiveMessageHandler);
  81. }
  82. else {
  83. window.removeEventListener('message', receiveMessageHandler);
  84. }
  85. // clean up
  86. return function() {
  87. window.removeEventListener('message', receiveMessageHandler);
  88. };
  89. }, [isOpened, receiveMessageHandler]);
  90. return (
  91. <Modal
  92. isOpen={isOpened}
  93. toggle={() => closeDrawioModal()}
  94. backdrop="static"
  95. className="drawio-modal grw-body-only-modal-expanded"
  96. size="xl"
  97. keyboard={false}
  98. >
  99. <ModalBody className="p-0">
  100. {/* Loading spinner */}
  101. <div className="w-100 h-100 position-absolute d-flex">
  102. <div className="mx-auto my-auto">
  103. <i className="fa fa-3x fa-spinner fa-pulse mx-auto text-muted"></i>
  104. </div>
  105. </div>
  106. {/* iframe */}
  107. { drawioUriWithParams != null && (
  108. <div className="w-100 h-100 position-absolute d-flex">
  109. { isOpened && (
  110. <iframe
  111. src={drawioUriWithParams.href}
  112. className="border-0 flex-grow-1"
  113. >
  114. </iframe>
  115. ) }
  116. </div>
  117. ) }
  118. </ModalBody>
  119. </Modal>
  120. );
  121. };