DrawioModal.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  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 { 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 = {
  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. };
  31. export const DrawioModal = (): JSX.Element => {
  32. const { data: drawioUri } = useDrawioUri();
  33. const { data: personalSettingsInfo } = usePersonalSettings({
  34. // make immutable
  35. revalidateIfStale: false,
  36. revalidateOnFocus: false,
  37. revalidateOnReconnect: false,
  38. });
  39. const { data: drawioModalData, close: closeDrawioModal } = useDrawioModal();
  40. const isOpened = drawioModalData?.isOpened ?? false;
  41. const drawioUriWithParams = useMemo(() => {
  42. if (drawioUri == null) {
  43. return undefined;
  44. }
  45. let url;
  46. try {
  47. url = new URL(drawioUri);
  48. }
  49. catch (err) {
  50. logger.debug(err);
  51. return undefined;
  52. }
  53. // refs: https://desk.draw.io/support/solutions/articles/16000042546-what-url-parameters-are-supported-
  54. url.searchParams.append('spin', '1');
  55. url.searchParams.append('embed', '1');
  56. url.searchParams.append('lang', getDiagramsNetLangCode(personalSettingsInfo?.lang || 'en'));
  57. url.searchParams.append('ui', 'atlas');
  58. url.searchParams.append('configure', '1');
  59. return url;
  60. }, [drawioUri, personalSettingsInfo?.lang]);
  61. const drawioCommunicationHelper = useMemo(() => {
  62. if (drawioUri == null) {
  63. return undefined;
  64. }
  65. return new DrawioCommunicationHelper(
  66. drawioUri,
  67. drawioConfig,
  68. { onClose: closeDrawioModal, onSave: drawioModalData?.onSave },
  69. );
  70. }, [closeDrawioModal, drawioModalData?.onSave, drawioUri]);
  71. const receiveMessageHandler = useCallback((event: MessageEvent) => {
  72. if (drawioModalData == null) {
  73. return;
  74. }
  75. drawioCommunicationHelper?.onReceiveMessage(event, drawioModalData.drawioMxFile);
  76. }, [drawioCommunicationHelper, drawioModalData]);
  77. useEffect(() => {
  78. if (isOpened) {
  79. window.addEventListener('message', receiveMessageHandler);
  80. }
  81. else {
  82. window.removeEventListener('message', receiveMessageHandler);
  83. }
  84. // clean up
  85. return function() {
  86. window.removeEventListener('message', receiveMessageHandler);
  87. };
  88. }, [isOpened, receiveMessageHandler]);
  89. return (
  90. <Modal
  91. isOpen={isOpened}
  92. toggle={() => closeDrawioModal()}
  93. backdrop="static"
  94. className="drawio-modal grw-body-only-modal-expanded"
  95. size="xl"
  96. keyboard={false}
  97. >
  98. <ModalBody className="p-0">
  99. {/* Loading spinner */}
  100. <div className="w-100 h-100 position-absolute d-flex">
  101. <div className="mx-auto my-auto">
  102. <i className="fa fa-3x fa-spinner fa-pulse mx-auto text-muted"></i>
  103. </div>
  104. </div>
  105. {/* iframe */}
  106. { drawioUriWithParams != null && (
  107. <div className="w-100 h-100 position-absolute d-flex">
  108. { isOpened && (
  109. <iframe
  110. src={drawioUriWithParams.href}
  111. className="border-0 flex-grow-1"
  112. >
  113. </iframe>
  114. ) }
  115. </div>
  116. ) }
  117. </ModalBody>
  118. </Modal>
  119. );
  120. };