UNPKG

@finos/legend-application-pure-ide

Version:
113 lines 12.2 kB
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