| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- import React, {
- useCallback, useEffect, useMemo, useRef, useState,
- } from 'react';
- import EventEmitter from 'events';
- import { useTranslation } from 'next-i18next';
- import { debounce } from 'throttle-debounce';
- import { CustomWindow } from '~/interfaces/global';
- import { IGraphViewer, isGraphViewer } from '~/interfaces/graph-viewer';
- import NotAvailableForGuest from './NotAvailableForGuest';
- type Props = {
- GraphViewer: IGraphViewer,
- drawioContent: string,
- rangeLineNumberOfMarkdown: { beginLineNumber: number, endLineNumber: number },
- isPreview?: boolean,
- }
- // It calls callback when GraphViewer is not null.
- // eslint-disable-next-line @typescript-eslint/ban-types
- const waitForGraphViewer = async(callback: Function) => {
- const MAX_WAIT_COUNT = 10; // no reason for 10
- for (let i = 0; i < MAX_WAIT_COUNT; i++) {
- if (isGraphViewer((window as CustomWindow).GraphViewer)) {
- callback((window as CustomWindow).GraphViewer);
- break;
- }
- // Sleep 500 ms
- // eslint-disable-next-line no-await-in-loop
- await new Promise<void>(r => setTimeout(() => r(), 500));
- }
- };
- const Drawio = (props: Props): JSX.Element => {
- const { t } = useTranslation();
- // Wrap with a function since GraphViewer is a function.
- // This applies when call setGraphViewer as well.
- const [GraphViewer, setGraphViewer] = useState<IGraphViewer | undefined>(() => (window as CustomWindow).GraphViewer);
- const { drawioContent, rangeLineNumberOfMarkdown, isPreview } = props;
- // const { open: openDrawioModal } = useDrawioModalForPage();
- const drawioContainerRef = useRef<HTMLDivElement>(null);
- const globalEmitter: EventEmitter = (window as CustomWindow).globalEmitter;
- const editButtonClickHandler = useCallback(() => {
- const { beginLineNumber, endLineNumber } = rangeLineNumberOfMarkdown;
- globalEmitter.emit('launchDrawioModal', beginLineNumber, endLineNumber);
- }, [rangeLineNumberOfMarkdown, globalEmitter]);
- const renderDrawio = useCallback((GraphViewer: IGraphViewer) => {
- if (drawioContainerRef.current == null) {
- return;
- }
- const mxgraphs = drawioContainerRef.current.getElementsByClassName('mxgraph');
- if (mxgraphs.length > 0) {
- // GROWI では、mxgraph element は最初のものをレンダリングする前提とする
- const div = mxgraphs[0];
- if (div != null) {
- div.innerHTML = '';
- GraphViewer.createViewerForElement(div);
- }
- }
- }, [drawioContainerRef]);
- const renderDrawioWithDebounce = useMemo(() => debounce(200, renderDrawio), [renderDrawio]);
- useEffect(() => {
- if (GraphViewer == null) {
- waitForGraphViewer((gv: IGraphViewer) => {
- setGraphViewer(() => gv);
- });
- return;
- }
- renderDrawioWithDebounce(GraphViewer);
- }, [renderDrawioWithDebounce, GraphViewer]);
- return (
- <div className="editable-with-drawio position-relative">
- { !isPreview && (
- <NotAvailableForGuest>
- <button type="button" className="drawio-iframe-trigger position-absolute btn btn-outline-secondary" onClick={editButtonClickHandler}>
- <i className="icon-note mr-1"></i>{t('Edit')}
- </button>
- </NotAvailableForGuest>
- ) }
- <div
- className="drawio"
- style={
- {
- borderRadius: 3,
- border: '1px solid #d7d7d7',
- margin: '20px 0',
- }
- }
- ref={drawioContainerRef}
- // eslint-disable-next-line react/no-danger
- dangerouslySetInnerHTML={{ __html: drawioContent }}
- >
- </div>
- </div>
- );
- };
- export default Drawio;
|