DrawioModal.tsx 5.3 KB

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