UNPKG

@finos/legend-extension-dsl-diagram

Version:
170 lines 19 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; /** * Copyright (c) 2025-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 { AnchorLinkIcon, CaretDownIcon, CenterFocusIcon, CircleIcon, CloseIcon, ContextMenu, CustomSelectorInput, DescriptionIcon, ControlledDropdownMenu, MenuContent, MenuContentDivider, MenuContentItem, MousePointerIcon, MoveIcon, ShapesIcon, ThinChevronDownIcon, ThinChevronLeftIcon, ThinChevronRightIcon, ThinChevronUpIcon, ZoomInIcon, ZoomOutIcon, clsx, useResizeDetector, createFilter, MarkdownTextViewer, } from '@finos/legend-art'; import { observer } from 'mobx-react-lite'; import { forwardRef, useEffect, useRef } from 'react'; import { DIAGRAM_INTERACTION_MODE, DIAGRAM_RELATIONSHIP_EDIT_MODE, DIAGRAM_ZOOM_LEVELS, DiagramRenderer, } from './DiagramRenderer.js'; import { at } from '@finos/legend-shared'; import { useCommands, } from '@finos/legend-application'; var DIAGRAM_VIEWER_MODES; (function (DIAGRAM_VIEWER_MODES) { DIAGRAM_VIEWER_MODES["DIAGRAM_VIEWER"] = "diagram-viewer"; DIAGRAM_VIEWER_MODES["MODELS_DOCUMENTATION"] = "models-documentation"; })(DIAGRAM_VIEWER_MODES || (DIAGRAM_VIEWER_MODES = {})); const DiagramCanvas = observer(forwardRef(function DiagramCanvas(props, _ref) { const { diagramViewerState, diagram, actions } = props; const { onQueryClass, onViewClassDocumentation, hasClassDocumentation, onGenerateAnchorForActivity, onChangeZone, } = actions; const ref = _ref; const descriptionText = diagramViewerState.currentDiagram?.description; const { width, height } = useResizeDetector({ refreshMode: 'debounce', refreshRate: 50, targetRef: ref, }); useEffect(() => { diagramViewerState.setExpandDescription(false); }, [diagramViewerState, diagramViewerState.currentDiagram]); useEffect(() => { const renderer = new DiagramRenderer(ref.current, diagram); diagramViewerState.setDiagramRenderer(renderer); diagramViewerState.setupDiagramRenderer(); renderer.render({ initial: true }); }, [ref, diagramViewerState, diagram]); useEffect(() => { if (diagramViewerState.isDiagramRendererInitialized) { diagramViewerState.diagramRenderer.refresh(); } }, [diagramViewerState, width, height]); const queryClass = () => { if (onQueryClass && diagramViewerState.contextMenuClassView) { onQueryClass(diagramViewerState.contextMenuClassView.class.value); } }; const viewClassDocumentation = () => { if (diagramViewerState.contextMenuClassView && hasClassDocumentation(diagramViewerState.contextMenuClassView.class.value.path)) { onViewClassDocumentation(diagramViewerState.contextMenuClassView.class.value.path); onChangeZone(onGenerateAnchorForActivity(DIAGRAM_VIEWER_MODES.MODELS_DOCUMENTATION)); } }; return (_jsxs(ContextMenu, { className: "data-space__viewer__diagram-viewer__canvas", content: _jsxs(MenuContent, { children: [_jsx(MenuContentItem, { onClick: queryClass, disabled: !diagramViewerState.contextMenuClassView, children: "Query" }), _jsx(MenuContentItem, { onClick: viewClassDocumentation, disabled: !diagramViewerState.contextMenuClassView || !hasClassDocumentation(diagramViewerState.contextMenuClassView.class.value.path), children: "See Model Documentation" })] }), disabled: !diagramViewerState.contextMenuClassView, menuProps: { elevation: 7 }, onClose: () => diagramViewerState.setContextMenuClassView(undefined), children: [diagramViewerState.showDescription && (_jsxs("div", { className: clsx('data-space__viewer__diagram-viewer__description', { 'data-space__viewer__diagram-viewer__description--expanded': diagramViewerState.expandDescription, }), children: [_jsx("button", { className: "data-space__viewer__diagram-viewer__description__close-btn", tabIndex: -1, title: "Hide Description", onClick: () => diagramViewerState.setShowDescription(false), children: _jsx(CloseIcon, {}) }), _jsx("div", { className: "data-space__viewer__diagram-viewer__description__title", children: diagramViewerState.currentDiagram?.title ? diagramViewerState.currentDiagram.title : 'Untitled' }), _jsx("div", { className: "data-space__viewer__diagram-viewer__description__content", children: descriptionText ? (_jsx(MarkdownTextViewer, { value: { value: descriptionText, } })) : (_jsx("div", { className: "data-space__viewer__diagram-viewer__description__content__placeholder", children: "(not specified)" })) }), _jsx("button", { className: "data-space__viewer__diagram-viewer__description__expand-btn", tabIndex: -1, title: diagramViewerState.expandDescription ? 'Collapse' : 'Expand', onClick: () => diagramViewerState.setExpandDescription(!diagramViewerState.expandDescription), children: diagramViewerState.expandDescription ? (_jsx(ThinChevronUpIcon, {})) : (_jsx(ThinChevronDownIcon, {})) })] })), _jsx("div", { ref: ref, className: clsx('diagram-canvas', diagramViewerState.diagramCursorClass), tabIndex: 0 })] })); })); const buildDiagramOption = (diagram) => ({ label: (_jsxs("div", { className: "data-space__viewer__diagram-viewer__header__navigation__selector__label", children: [_jsx(ShapesIcon, { className: "data-space__viewer__diagram-viewer__header__navigation__selector__icon" }), _jsx("div", { className: "data-space__viewer__diagram-viewer__header__navigation__selector__title", children: diagram.title ? diagram.title : 'Untitled' })] })), value: diagram, }); const DiagramViewerHeader = observer((props) => { const { applicationStore, diagramViewerState, actions } = props; const { onSyncZoneWithNavigation } = actions; const diagramOptions = diagramViewerState.diagrams.map(buildDiagramOption); const selectedDiagramOption = diagramViewerState.currentDiagram ? buildDiagramOption(diagramViewerState.currentDiagram) : null; const onDiagramOptionChange = (option) => { if (option.value !== diagramViewerState.currentDiagram) { diagramViewerState.setCurrentDiagram(option.value); } }; const diagramFilterOption = createFilter({ ignoreCase: true, ignoreAccents: false, stringify: (option) => option.data.value.title, }); const createModeSwitcher = (editMode, relationshipMode) => () => diagramViewerState.diagramRenderer.changeMode(editMode, relationshipMode); const createCenterZoomer = (zoomLevel) => () => { diagramViewerState.diagramRenderer.zoomCenter(zoomLevel / 100); }; const zoomToFit = () => diagramViewerState.diagramRenderer.zoomToFit(); return (_jsxs("div", { className: "data-space__viewer__diagram-viewer__header", children: [_jsxs("div", { className: "data-space__viewer__diagram-viewer__header__navigation", children: [_jsx(CustomSelectorInput, { className: "data-space__viewer__diagram-viewer__header__navigation__selector", options: diagramOptions, onChange: onDiagramOptionChange, value: selectedDiagramOption, placeholder: "Search for a diagram", darkMode: !applicationStore.layoutService .TEMPORARY__isLightColorThemeEnabled, filterOption: diagramFilterOption }), _jsx("div", { className: "data-space__viewer__diagram-viewer__header__navigation__pager", children: _jsx("input", { className: "data-space__viewer__diagram-viewer__header__navigation__pager__input input--dark", value: diagramViewerState.currentDiagramIndex, type: "number", onChange: (event) => { const value = parseInt(event.target.value, 10); if (isNaN(value) || value < 1 || value > diagramViewerState.diagrams.length) { return; } diagramViewerState.setCurrentDiagram(at(diagramViewerState.diagrams, value - 1)); } }) }), _jsxs("div", { className: "data-space__viewer__diagram-viewer__header__navigation__pager__count", children: ["/", diagramViewerState.diagrams.length] })] }), _jsx("div", { className: "data-space__viewer__diagram-viewer__header__actions", children: diagramViewerState.isDiagramRendererInitialized && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "data-space__viewer__diagram-viewer__header__group", children: [_jsx("button", { className: "data-space__viewer__diagram-viewer__header__tool", tabIndex: -1, onClick: () => { if (diagramViewerState.currentDiagram) { onSyncZoneWithNavigation(diagramViewerState.currentDiagram); } }, title: "Copy Link", children: _jsx(AnchorLinkIcon, {}) }), _jsx("button", { className: "data-space__viewer__diagram-viewer__header__tool", tabIndex: -1, onClick: () => diagramViewerState.diagramRenderer.recenter(), title: "Recenter (R)", children: _jsx(CenterFocusIcon, { className: "data-space__viewer__diagram-viewer__icon--recenter" }) }), _jsx("button", { className: clsx('data-space__viewer__diagram-viewer__header__tool', { 'data-space__viewer__diagram-viewer__header__tool--active': diagramViewerState.showDescription, }), tabIndex: -1, onClick: () => diagramViewerState.setShowDescription(!diagramViewerState.showDescription), title: "Toggle Description (D)", children: _jsx(DescriptionIcon, { className: "data-space__viewer__diagram-viewer__icon--description" }) })] }), _jsx("div", { className: "data-space__viewer__diagram-viewer__header__group__separator" }), _jsxs("div", { className: "data-space__viewer__diagram-viewer__header__group", children: [_jsx("button", { className: clsx('data-space__viewer__diagram-viewer__header__tool', { 'data-space__viewer__diagram-viewer__header__tool--active': diagramViewerState.diagramRenderer.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: "data-space__viewer__diagram-viewer__icon--layout" }) }), _jsx("button", { className: clsx('data-space__viewer__diagram-viewer__header__tool', { 'data-space__viewer__diagram-viewer__header__tool--active': diagramViewerState.diagramRenderer.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: "data-space__viewer__diagram-viewer__icon--pan" }) }), _jsx("button", { className: clsx('data-space__viewer__diagram-viewer__header__tool', { 'data-space__viewer__diagram-viewer__header__tool--active': diagramViewerState.diagramRenderer.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: "data-space__viewer__diagram-viewer__icon--zoom-in" }) }), _jsx("button", { className: clsx('data-space__viewer__diagram-viewer__header__tool', { 'data-space__viewer__diagram-viewer__header__tool--active': diagramViewerState.diagramRenderer.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: "data-space__viewer__diagram-viewer__icon--zoom-out" }) })] }), _jsx("div", { className: "data-space__viewer__diagram-viewer__header__group__separator" }), _jsxs(ControlledDropdownMenu, { className: "data-space__viewer__diagram-viewer__header__group data-space__viewer__diagram-viewer__header__dropdown", title: "Zoom...", content: _jsxs(MenuContent, { children: [_jsx(MenuContentItem, { className: "data-space__viewer__diagram-viewer__header__zoomer__dropdown__menu__item", onClick: zoomToFit, children: "Fit" }), _jsx(MenuContentDivider, {}), DIAGRAM_ZOOM_LEVELS.map((zoomLevel) => (_jsxs(MenuContentItem, { className: "data-space__viewer__diagram-viewer__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: "data-space__viewer__diagram-viewer__header__dropdown__label data-space__viewer__diagram-viewer__header__zoomer__dropdown__label", children: [Math.round(diagramViewerState.diagramRenderer.zoom * 100), "%"] }), _jsx("div", { className: "data-space__viewer__diagram-viewer__header__dropdown__trigger data-space__viewer__diagram-viewer__header__zoomer__dropdown__trigger", children: _jsx(CaretDownIcon, {}) })] })] })) })] })); }); export const DiagramPlaceholder = (props) => (_jsx("div", { className: "diagram-viewer__placeholder", children: props.message })); export const DiagramViewer = observer((props) => { const { diagramViewerState, applicationStore, title, actions } = props; const { onSyncZoneWithNavigation, onGenerateAnchorForActivity, onSetWikiPageAnchor, onUnsetWikiPageAnchor, onChangeZone, } = actions; const diagrams = diagramViewerState.diagrams; const sectionRef = useRef(null); const anchor = onGenerateAnchorForActivity(DIAGRAM_VIEWER_MODES.DIAGRAM_VIEWER); useCommands(diagramViewerState); useEffect(() => { if (sectionRef.current) { onSetWikiPageAnchor(anchor, sectionRef.current); } return () => onUnsetWikiPageAnchor(anchor); }, [onSetWikiPageAnchor, onUnsetWikiPageAnchor, anchor]); const diagramCanvasRef = useRef(null); const previousDiagram = diagramViewerState.previousDiagram; const nextDiagram = diagramViewerState.nextDiagram; const showPreviousDiagram = () => { if (previousDiagram) { diagramViewerState.setCurrentDiagram(previousDiagram); onSyncZoneWithNavigation(previousDiagram); } }; const showNextDiagram = () => { if (nextDiagram) { diagramViewerState.setCurrentDiagram(nextDiagram); onSyncZoneWithNavigation(nextDiagram); } }; return (_jsxs("div", { ref: sectionRef, className: "data-space__viewer__wiki__section", children: [_jsx("div", { className: "data-space__viewer__wiki__section__header", children: _jsxs("div", { className: "data-space__viewer__wiki__section__header__label", children: [title ?? 'Diagrams', _jsx("button", { className: "data-space__viewer__wiki__section__header__anchor", tabIndex: -1, onClick: () => onChangeZone(anchor, true), children: _jsx(AnchorLinkIcon, {}) })] }) }), _jsxs("div", { className: "data-space__viewer__wiki__section__content", children: [diagrams.length > 0 && (_jsxs("div", { className: "data-space__viewer__diagram-viewer", children: [_jsx(DiagramViewerHeader, { applicationStore: applicationStore, diagramViewerState: diagramViewerState, actions: actions }), _jsx("div", { className: "data-space__viewer__diagram-viewer__carousel", children: _jsxs("div", { className: "data-space__viewer__diagram-viewer__carousel__frame", children: [_jsx("div", { className: "data-space__viewer__diagram-viewer__carousel__frame__display", children: diagramViewerState.currentDiagram && (_jsx(DiagramCanvas, { diagramViewerState: diagramViewerState, diagram: diagramViewerState.currentDiagram.diagram, ref: diagramCanvasRef, actions: actions })) }), _jsx("button", { className: "data-space__viewer__diagram-viewer__carousel__frame__navigator data-space__viewer__diagram-viewer__carousel__frame__navigator--back", tabIndex: -1, title: `Previous - ${previousDiagram?.title ? previousDiagram.title : '(untitled)'} (⇦)`, disabled: !previousDiagram, onClick: showPreviousDiagram, children: _jsx(ThinChevronLeftIcon, {}) }), _jsx("button", { className: "data-space__viewer__diagram-viewer__carousel__frame__navigator data-space__viewer__diagram-viewer__carousel__frame__navigator--next", tabIndex: -1, title: `Next - ${nextDiagram?.title ? nextDiagram.title : '(untitled)'} (⇨)`, disabled: !nextDiagram, onClick: showNextDiagram, children: _jsx(ThinChevronRightIcon, {}) }), _jsx("div", { className: "data-space__viewer__diagram-viewer__carousel__frame__indicators", children: _jsx("div", { className: "data-space__viewer__diagram-viewer__carousel__frame__indicators__notch", children: diagrams.map((diagram) => (_jsx("button", { className: clsx('data-space__viewer__diagram-viewer__carousel__frame__indicator', { 'data-space__viewer__diagram-viewer__carousel__frame__indicator--active': diagramViewerState.currentDiagram === diagram, }), title: `View Diagram - ${diagram.title ? diagram.title : '(untitled)'}`, onClick: () => { diagramViewerState.setCurrentDiagram(diagram); onSyncZoneWithNavigation(diagram); }, children: _jsx(CircleIcon, {}) }, diagram.uuid))) }) })] }) })] })), !diagrams.length && _jsx(DiagramPlaceholder, { message: "(not specified)" })] })] })); }); //# sourceMappingURL=DiagramViewer.js.map