@finos/legend-application-pure-ide
Version:
Legend Pure IDE application core
113 lines • 12.2 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
/**
* Copyright (c) 2020-present, Goldman Sachs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { forwardRef, useCallback, useEffect, useRef } from 'react';
import { observer } from 'mobx-react-lite';
import { DIAGRAM_ALIGNER_OPERATOR, DiagramRenderer, DIAGRAM_ZOOM_LEVELS, DIAGRAM_INTERACTION_MODE, DIAGRAM_RELATIONSHIP_EDIT_MODE, } from '@finos/legend-extension-dsl-diagram/application';
import { Point } from '@finos/legend-extension-dsl-diagram/graph';
import { useDrop } from 'react-dnd';
import { CONCEPT_TREE_DND_TYPE } from '../side-bar/ConceptTreeExplorer.js';
import { ConceptNode } from '../../server/models/ConceptTree.js';
import { flowResult } from 'mobx';
import { useApplicationStore, useCommands } from '@finos/legend-application';
import { AlignBottomIcon, AlignCenterIcon, AlignEndIcon, AlignMiddleIcon, AlignStartIcon, AlignTopIcon, CaretDownIcon, clsx, DistributeHorizontalIcon, DistributeVerticalIcon, ControlledDropdownMenu, GoToFileIcon, MenuContent, MenuContentDivider, MenuContentItem, MousePointerIcon, MoveIcon, useResizeDetector, ZoomInIcon, ZoomOutIcon, } from '@finos/legend-art';
import { usePureIDEStore } from '../PureIDEStoreProvider.js';
import { FileCoordinate } from '../../server/models/File.js';
const DiagramEditorDiagramCanvas = observer(forwardRef(function DiagramEditorDiagramCanvas(props, _ref) {
const { diagramEditorState } = props;
const applicationStore = useApplicationStore();
const diagram = diagramEditorState.diagram;
const ref = _ref;
const { width, height } = useResizeDetector({
refreshMode: 'debounce',
refreshRate: 50,
targetRef: ref,
});
useEffect(() => {
const renderer = new DiagramRenderer(ref.current, diagram);
diagramEditorState.setRenderer(renderer);
diagramEditorState.setupRenderer();
renderer.render({ initial: true });
}, [ref, diagramEditorState, diagram]);
useEffect(() => {
// since after the diagram render is initialized, we start
// showing the toolbar and the header, which causes the auto-zoom fit
// to be off, we need to call this method again
if (diagramEditorState.isDiagramRendererInitialized) {
diagramEditorState.renderer.render({ initial: true });
}
}, [diagramEditorState, diagramEditorState.isDiagramRendererInitialized]);
useEffect(() => {
if (diagramEditorState.isDiagramRendererInitialized) {
diagramEditorState.renderer.refresh();
}
}, [diagramEditorState, width, height]);
// Drag and Drop
const handleDrop = useCallback((item, monitor) => {
if (item instanceof ConceptNode) {
const dropPosition = monitor.getClientOffset();
const position = dropPosition
? diagramEditorState.renderer.canvasCoordinateToModelCoordinate(diagramEditorState.renderer.eventCoordinateToCanvasCoordinate(new Point(dropPosition.x, dropPosition.y)))
: undefined;
flowResult(diagramEditorState.addClassView(item.li_attr.pureId, position)).catch(applicationStore.alertUnhandledError);
}
}, [applicationStore, diagramEditorState]);
const [, dropConnector] = useDrop(() => ({
accept: CONCEPT_TREE_DND_TYPE.CLASS,
drop: (item, monitor) => handleDrop(item, monitor),
}), [handleDrop]);
dropConnector(ref);
return (_jsx("div", { ref: ref, className: clsx('diagram-canvas diagram-editor__canvas', diagramEditorState.diagramCursorClass), tabIndex: 0 }));
}));
const DiagramEditorHeader = observer((props) => {
const { diagramEditorState } = props;
const ideStore = usePureIDEStore();
const applicationStore = useApplicationStore();
const goToFile = () => {
flowResult(ideStore.loadFile(diagramEditorState.filePath, new FileCoordinate(diagramEditorState.filePath, diagramEditorState.fileLine, diagramEditorState.fileColumn))).catch(applicationStore.alertUnhandledError);
};
const createCenterZoomer = (zoomLevel) => () => {
diagramEditorState.renderer.zoomCenter(zoomLevel / 100);
};
const zoomToFit = () => diagramEditorState.renderer.zoomToFit();
const isAlignerDisabled = diagramEditorState.renderer.selectedClasses.length < 2;
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "diagram-editor__header__group", children: [_jsx("button", { className: "diagram-editor__header__action diagram-editor__header__group__action", title: "Align left", disabled: isAlignerDisabled, tabIndex: -1, onClick: () => diagramEditorState.renderer.align(DIAGRAM_ALIGNER_OPERATOR.ALIGN_LEFT), children: _jsx(AlignStartIcon, { className: "diagram-editor__icon--aligner" }) }), _jsx("button", { className: "diagram-editor__header__action diagram-editor__header__group__action", title: "Align center", disabled: isAlignerDisabled, tabIndex: -1, onClick: () => diagramEditorState.renderer.align(DIAGRAM_ALIGNER_OPERATOR.ALIGN_CENTER), children: _jsx(AlignCenterIcon, { className: "diagram-editor__icon--aligner" }) }), _jsx("button", { className: "diagram-editor__header__action diagram-editor__header__group__action", title: "Align right", disabled: isAlignerDisabled, tabIndex: -1, onClick: () => diagramEditorState.renderer.align(DIAGRAM_ALIGNER_OPERATOR.ALIGN_RIGHT), children: _jsx(AlignEndIcon, { className: "diagram-editor__icon--aligner" }) })] }), _jsx("div", { className: "diagram-editor__header__group__separator" }), _jsxs("div", { className: "diagram-editor__header__group", children: [_jsx("button", { className: "diagram-editor__header__action diagram-editor__header__group__action", title: "Align top", disabled: isAlignerDisabled, tabIndex: -1, onClick: () => diagramEditorState.renderer.align(DIAGRAM_ALIGNER_OPERATOR.ALIGN_TOP), children: _jsx(AlignTopIcon, { className: "diagram-editor__icon--aligner" }) }), _jsx("button", { className: "diagram-editor__header__action diagram-editor__header__group__action", title: "Align middle", disabled: isAlignerDisabled, tabIndex: -1, onClick: () => diagramEditorState.renderer.align(DIAGRAM_ALIGNER_OPERATOR.ALIGN_MIDDLE), children: _jsx(AlignMiddleIcon, { className: "diagram-editor__icon--aligner" }) }), _jsx("button", { className: "diagram-editor__header__action diagram-editor__header__group__action", title: "Align bottom", disabled: isAlignerDisabled, tabIndex: -1, onClick: () => diagramEditorState.renderer.align(DIAGRAM_ALIGNER_OPERATOR.ALIGN_BOTTOM), children: _jsx(AlignBottomIcon, { className: "diagram-editor__icon--aligner" }) })] }), _jsx("div", { className: "diagram-editor__header__group__separator" }), _jsxs("div", { className: "diagram-editor__header__group", children: [_jsx("button", { className: "diagram-editor__header__action diagram-editor__header__group__action", title: "Space horizontally", disabled: isAlignerDisabled, tabIndex: -1, onClick: () => diagramEditorState.renderer.align(DIAGRAM_ALIGNER_OPERATOR.SPACE_HORIZONTALLY), children: _jsx(DistributeHorizontalIcon, { className: "diagram-editor__icon--aligner" }) }), _jsx("button", { className: "diagram-editor__header__action diagram-editor__header__group__action", title: "Space vertically", disabled: isAlignerDisabled, tabIndex: -1, onClick: () => diagramEditorState.renderer.align(DIAGRAM_ALIGNER_OPERATOR.SPACE_VERTICALLY), children: _jsx(DistributeVerticalIcon, { className: "diagram-editor__icon--aligner" }) })] }), _jsxs(ControlledDropdownMenu, { className: "diagram-editor__header__dropdown", title: "Zoom...", content: _jsxs(MenuContent, { children: [_jsx(MenuContentItem, { className: "diagram-editor__header__zoomer__dropdown__menu__item", onClick: zoomToFit, children: "Fit" }), _jsx(MenuContentDivider, {}), DIAGRAM_ZOOM_LEVELS.map((zoomLevel) => (_jsxs(MenuContentItem, { className: "diagram-editor__header__zoomer__dropdown__menu__item", onClick: createCenterZoomer(zoomLevel), children: [zoomLevel, "%"] }, zoomLevel)))] }), menuProps: {
anchorOrigin: { vertical: 'bottom', horizontal: 'right' },
transformOrigin: { vertical: 'top', horizontal: 'right' },
elevation: 7,
}, children: [_jsxs("div", { className: "diagram-editor__header__dropdown__label diagram-editor__header__zoomer__dropdown__label", children: [Math.round(diagramEditorState.renderer.zoom * 100), "%"] }), _jsx("div", { className: "diagram-editor__header__dropdown__trigger diagram-editor__header__zoomer__dropdown__trigger", children: _jsx(CaretDownIcon, {}) })] }), _jsx("div", { className: "diagram-editor__header__actions", children: _jsx("button", { className: "diagram-editor__header__action", tabIndex: -1, onClick: goToFile, children: _jsx(GoToFileIcon, { className: "diagram-editor__icon--go-to-file" }) }) })] }));
});
const DiagramEditorToolPanel = observer((props) => {
const { diagramEditorState } = props;
const renderer = diagramEditorState.renderer;
const createModeSwitcher = (editMode, relationshipMode) => () => renderer.changeMode(editMode, relationshipMode);
return (_jsxs("div", { className: "diagram-editor__tools", children: [_jsx("button", { className: clsx('diagram-editor__tool', {
'diagram-editor__tool--active': renderer.interactionMode === DIAGRAM_INTERACTION_MODE.LAYOUT,
}), tabIndex: -1, onClick: createModeSwitcher(DIAGRAM_INTERACTION_MODE.LAYOUT, DIAGRAM_RELATIONSHIP_EDIT_MODE.NONE), title: "View Tool (V)", children: _jsx(MousePointerIcon, { className: "diagram-editor__icon--layout" }) }), _jsx("button", { className: clsx('diagram-editor__tool', {
'diagram-editor__tool--active': renderer.interactionMode === DIAGRAM_INTERACTION_MODE.PAN,
}), tabIndex: -1, onClick: createModeSwitcher(DIAGRAM_INTERACTION_MODE.PAN, DIAGRAM_RELATIONSHIP_EDIT_MODE.NONE), title: "Pan Tool (M)", children: _jsx(MoveIcon, { className: "diagram-editor__icon--pan" }) }), _jsx("button", { className: clsx('diagram-editor__tool', {
'diagram-editor__tool--active': renderer.interactionMode === DIAGRAM_INTERACTION_MODE.ZOOM_IN,
}), tabIndex: -1, title: "Zoom In (Z)", onClick: createModeSwitcher(DIAGRAM_INTERACTION_MODE.ZOOM_IN, DIAGRAM_RELATIONSHIP_EDIT_MODE.NONE), children: _jsx(ZoomInIcon, { className: "diagram-editor__icon--zoom-in" }) }), _jsx("button", { className: clsx('diagram-editor__tool', {
'diagram-editor__tool--active': renderer.interactionMode === DIAGRAM_INTERACTION_MODE.ZOOM_OUT,
}), tabIndex: -1, title: "Zoom Out (Z)", onClick: createModeSwitcher(DIAGRAM_INTERACTION_MODE.ZOOM_OUT, DIAGRAM_RELATIONSHIP_EDIT_MODE.NONE), children: _jsx(ZoomOutIcon, { className: "diagram-editor__icon--zoom-out" }) })] }));
});
export const DiagramEditor = observer((props) => {
const { diagramEditorState } = props;
const diagramCanvasRef = useRef(null);
useCommands(diagramEditorState);
return (_jsxs("div", { className: "diagram-editor", children: [_jsx("div", { className: "diagram-editor__header", children: diagramEditorState.isDiagramRendererInitialized && (_jsx(DiagramEditorHeader, { diagramEditorState: diagramEditorState })) }), _jsx("div", { className: "diagram-editor__content", children: _jsxs("div", { className: "diagram-editor__stage", children: [diagramEditorState.isDiagramRendererInitialized && (_jsx(DiagramEditorToolPanel, { diagramEditorState: diagramEditorState })), _jsx(DiagramEditorDiagramCanvas, { diagramEditorState: diagramEditorState, ref: diagramCanvasRef })] }) })] }));
});
//# sourceMappingURL=DiagramEditor.js.map