import React, { useCallback, useEffect, useMemo, type JSX, } from 'react'; import { Lang } from '@growi/core'; import { useCodeMirrorEditorIsolated } from '@growi/editor/dist/client/stores/codemirror-editor'; import { useDrawioModalForEditorStatus, useDrawioModalForEditorActions } from '@growi/editor/dist/states/modal/drawio-for-editor'; import { LoadingSpinner } from '@growi/ui/dist/components'; import { Modal, ModalBody, } from 'reactstrap'; import { replaceFocusedDrawioWithEditor, getMarkdownDrawioMxfile } from '~/client/components/PageEditor/markdown-drawio-util-for-editor'; import { useRendererConfig } from '~/states/server-configurations'; import { useDrawioModalActions, useDrawioModalStatus } from '~/states/ui/modal/drawio'; import { useSWRxPersonalSettings } from '~/stores/personal-settings'; import loggerFactory from '~/utils/logger'; import { type DrawioConfig, DrawioCommunicationHelper } from './DrawioCommunicationHelper'; const logger = loggerFactory('growi:components:DrawioModal'); // https://docs.google.com/spreadsheets/d/1FoYdyEraEQuWofzbYCDPKN7EdKgS_2ZrsDrOA8scgwQ const DIAGRAMS_NET_LANG_MAP = { en_US: 'en', ja_JP: 'ja', zh_CN: 'zh', fr_FR: 'fr', }; export const getDiagramsNetLangCode = (lang: Lang): string => { return DIAGRAMS_NET_LANG_MAP[lang]; }; const headerColor = '#334455'; const fontFamily = "-apple-system, BlinkMacSystemFont, 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif"; const drawioConfig: DrawioConfig = { css: ` .geMenubarContainer { background-color: ${headerColor} !important; } .geMenubar { background-color: ${headerColor} !important; } .geEditor { font-family: ${fontFamily} !important; } html td.mxPopupMenuItem { font-family: ${fontFamily} !important; font-size: 8pt !important; } `, customFonts: ['Charter'], compressXml: true, }; const DrawioModalSubstance = (): JSX.Element => { const { drawioUri } = useRendererConfig(); const { data: personalSettingsInfo } = useSWRxPersonalSettings({ // make immutable revalidateIfStale: false, revalidateOnFocus: false, revalidateOnReconnect: false, }); const drawioModalData = useDrawioModalStatus(); const { close: closeDrawioModal } = useDrawioModalActions(); const drawioModalDataInEditor = useDrawioModalForEditorStatus(); const { close: closeDrawioModalInEditor } = useDrawioModalForEditorActions(); const editorKey = drawioModalDataInEditor?.editorKey ?? null; const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(editorKey); const editor = codeMirrorEditor?.view; const isOpenedInEditor = (drawioModalDataInEditor?.isOpened ?? false) && (editor != null); const isOpened = drawioModalData?.isOpened ?? false; // Memoize URI with parameters calculation const drawioUriWithParams = useMemo(() => { if (drawioUri === '') { return undefined; } let url; try { url = new URL(drawioUri); } catch (err) { logger.debug(err); return undefined; } // refs: https://desk.draw.io/support/solutions/articles/16000042546-what-url-parameters-are-supported- url.searchParams.append('spin', '1'); url.searchParams.append('embed', '1'); url.searchParams.append('lang', getDiagramsNetLangCode(personalSettingsInfo?.lang ?? Lang.en_US)); url.searchParams.append('ui', 'atlas'); url.searchParams.append('configure', '1'); return url; }, [drawioUri, personalSettingsInfo?.lang]); // Memoize communication helper with inline handlers to avoid dependency issues const drawioCommunicationHelper = useMemo(() => { if (drawioUri === '') { return undefined; } const saveHandler = editor != null ? (drawioMxFile: string) => replaceFocusedDrawioWithEditor(editor, drawioMxFile) : drawioModalData?.onSave; const closeHandler = isOpened ? closeDrawioModal : closeDrawioModalInEditor; return new DrawioCommunicationHelper( drawioUri, drawioConfig, { onClose: closeHandler, onSave: saveHandler }, ); }, [drawioUri, editor, drawioModalData?.onSave, isOpened, closeDrawioModal, closeDrawioModalInEditor]); const receiveMessageHandler = useCallback((event: MessageEvent) => { if (drawioModalData == null || drawioCommunicationHelper == null) { return; } const drawioMxFile = editor != null ? getMarkdownDrawioMxfile(editor) : drawioModalData.drawioMxFile; drawioCommunicationHelper.onReceiveMessage(event, drawioMxFile ?? null); }, [drawioCommunicationHelper, drawioModalData, editor]); // Memoize toggle handler const toggleHandler = useCallback(() => { if (isOpened) { closeDrawioModal(); } else { closeDrawioModalInEditor(); } }, [isOpened, closeDrawioModal, closeDrawioModalInEditor]); useEffect(() => { if (isOpened || isOpenedInEditor) { window.addEventListener('message', receiveMessageHandler); } else { window.removeEventListener('message', receiveMessageHandler); } // clean up return function() { window.removeEventListener('message', receiveMessageHandler); }; }, [isOpened, isOpenedInEditor, receiveMessageHandler]); return ( {/* Loading spinner */}
{/* iframe */} { drawioUriWithParams != null && (
{ (isOpened || isOpenedInEditor) && ( ) }
) }
); }; export const DrawioModal = (): JSX.Element => { return ; };