UNPKG

@finos/legend-extension-dsl-diagram

Version:
341 lines 15.6 kB
/** * 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 { computed, action, makeObservable, observable } from 'mobx'; import { guaranteeNonNullable, guaranteeType, isType, uuid, } from '@finos/legend-shared'; import { DIAGRAM_INTERACTION_MODE, } from '../../components/DiagramRenderer.js'; import { PanelDisplayState } from '@finos/legend-art'; import { GenericTypeExplicitReference, Property, GenericType, PropertyExplicitReference, isElementReadOnly, Multiplicity, PrimitiveType, } from '@finos/legend-graph'; import { ClassEditorState, ElementEditorState, class_addProperty, } from '@finos/legend-application-studio'; import { Diagram } from '../../graph/metamodel/pure/packageableElements/diagram/DSL_Diagram_Diagram.js'; import { DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY } from '../../__lib__/studio/DSL_Diagram_LegendStudioCommand.js'; export class DiagramEditorSidePanelState { uuid = uuid(); editorStore; diagramEditorState; constructor(editorStore, diagramEditorState) { this.editorStore = editorStore; this.diagramEditorState = diagramEditorState; } } export var DIAGRAM_EDITOR_SIDE_PANEL_TAB; (function (DIAGRAM_EDITOR_SIDE_PANEL_TAB) { DIAGRAM_EDITOR_SIDE_PANEL_TAB["ELEMENT"] = "ELEMENT"; DIAGRAM_EDITOR_SIDE_PANEL_TAB["VIEW"] = "VIEW"; })(DIAGRAM_EDITOR_SIDE_PANEL_TAB || (DIAGRAM_EDITOR_SIDE_PANEL_TAB = {})); export class DiagramEditorClassViewEditorSidePanelState extends DiagramEditorSidePanelState { classEditorState; classView; selectedTab = DIAGRAM_EDITOR_SIDE_PANEL_TAB.ELEMENT; constructor(editorStore, diagramEditorState, classView) { super(editorStore, diagramEditorState); makeObservable(this, { selectedTab: observable, setSelectedTab: action, }); this.classView = classView; this.classEditorState = guaranteeType(this.editorStore.tabManagerState.tabs.find((elementState) => isType(elementState, ClassEditorState) && elementState.element === classView.class.value) ?? new ClassEditorState(this.editorStore, classView.class.value), ClassEditorState); } setSelectedTab(val) { this.selectedTab = val; } } export class DiagramEditorInlineClassCreatorState { diagramEditorState; point; constructor(diagramEditorState, point) { this.diagramEditorState = diagramEditorState; this.point = point; } } export class DiagramEditorInlineClassRenamerState { diagramEditorState; classView; point; constructor(diagramEditorState, classView, point) { this.diagramEditorState = diagramEditorState; this.classView = classView; this.point = point; } } export class DiagramEditorInlinePropertyEditorState { diagramEditorState; property; point; isEditingPropertyView; constructor(diagramEditorState, property, point, isEditingPropertyView) { this.diagramEditorState = diagramEditorState; this.property = PropertyExplicitReference.create(property); this.point = point; this.isEditingPropertyView = isEditingPropertyView; } } export class DiagramEditorState extends ElementEditorState { _renderer; showHotkeyInfosModal = false; sidePanelDisplayState = new PanelDisplayState({ initial: 0, default: 500, snap: 100, }); sidePanelState; inlinePropertyEditorState; inlineClassCreatorState; inlineClassRenamerState; showContextMenu = false; contextMenuClassView; constructor(editorStore, element) { super(editorStore, element); makeObservable(this, { _renderer: observable, showHotkeyInfosModal: observable, sidePanelDisplayState: observable, sidePanelState: observable, inlinePropertyEditorState: observable, inlineClassCreatorState: observable, inlineClassRenamerState: observable, showContextMenu: observable, contextMenuClassView: observable, renderer: computed, diagram: computed, isDiagramRendererInitialized: computed, setShowHotkeyInfosModal: action, setRenderer: action, setSidePanelState: action, setInlinePropertyEditorState: action, setInlineClassCreatorState: action, setInlineClassRenamerState: action, setShowContextMenu: action, setContextMenuClassView: action, reprocess: action, }); } get diagram() { return guaranteeType(this.element, Diagram, 'Element inside diagram editor state must be a diagram'); } get renderer() { return guaranteeNonNullable(this._renderer, `Diagram renderer must be initialized (this is likely caused by calling this method at the wrong place)`); } get isDiagramRendererInitialized() { return Boolean(this._renderer); } // NOTE: we have tried to use React to control the cursor and // could not overcome the jank/lag problem, so we settle with CSS-based approach // See https://css-tricks.com/using-css-cursors/ // See https://developer.mozilla.org/en-US/docs/Web/CSS/cursor get diagramCursorClass() { if (!this.isDiagramRendererInitialized) { return ''; } if (this.renderer.middleClick || this.renderer.rightClick) { return 'diagram-editor__cursor--grabbing'; } switch (this.renderer.interactionMode) { case DIAGRAM_INTERACTION_MODE.ADD_CLASS: { return !this.isReadOnly ? 'diagram-editor__cursor--add' : ''; } case DIAGRAM_INTERACTION_MODE.PAN: { return this.renderer.leftClick ? 'diagram-editor__cursor--grabbing' : 'diagram-editor__cursor--grab'; } case DIAGRAM_INTERACTION_MODE.ZOOM_IN: { return 'diagram-editor__cursor--zoom-in'; } case DIAGRAM_INTERACTION_MODE.ZOOM_OUT: { return 'diagram-editor__cursor--zoom-out'; } case DIAGRAM_INTERACTION_MODE.ADD_RELATIONSHIP: { if (this.isReadOnly) { return ''; } if (this.renderer.mouseOverClassView && this.renderer.selectionStart) { return 'diagram-editor__cursor--add'; } return 'diagram-editor__cursor--crosshair'; } case DIAGRAM_INTERACTION_MODE.LAYOUT: { if (this.renderer.selectionStart) { return 'diagram-editor__cursor--crosshair'; } else if (this.renderer.mouseOverClassCorner || this.renderer.selectedClassCorner) { return 'diagram-editor__cursor--resize'; } else if (this.renderer.mouseOverClassProperty) { return this.isReadOnly || isElementReadOnly(this.renderer.mouseOverClassProperty._OWNER) ? 'diagram-editor__cursor--not-allowed' : 'diagram-editor__cursor--text'; } else if (this.renderer.mouseOverPropertyHolderViewLabel) { return this.isReadOnly || isElementReadOnly(this.renderer.mouseOverPropertyHolderViewLabel.property.value ._OWNER) ? 'diagram-editor__cursor--not-allowed' : 'diagram-editor__cursor--text'; } else if (this.renderer.mouseOverClassName) { return this.isReadOnly || isElementReadOnly(this.renderer.mouseOverClassName.class.value) ? 'diagram-editor__cursor--not-allowed' : 'diagram-editor__cursor--text'; } else if (this.renderer.mouseOverClassView) { return 'diagram-editor__cursor--pointer'; } return ''; } default: return ''; } } setRenderer(val) { this._renderer = val; } setShowHotkeyInfosModal(val) { this.showHotkeyInfosModal = val; } setSidePanelState(val) { this.sidePanelState = val; } setInlinePropertyEditorState(val) { this.inlinePropertyEditorState = val; } setInlineClassRenamerState(val) { this.inlineClassRenamerState = val; } setInlineClassCreatorState(val) { this.inlineClassCreatorState = val; } setShowContextMenu(val) { this.showContextMenu = val; } setContextMenuClassView(val) { this.contextMenuClassView = val; } closeContextMenu() { this.setShowContextMenu(false); } setupRenderer() { this.renderer.setIsReadOnly(this.isReadOnly); const handleEditClassView = (classView) => { this.setSidePanelState(new DiagramEditorClassViewEditorSidePanelState(this.editorStore, this, classView)); this.sidePanelDisplayState.open(); }; this.renderer.onClassViewDoubleClick = handleEditClassView; this.renderer.handleEditClassView = handleEditClassView; const createNewClassView = (point) => { if (!this.isReadOnly) { this.setInlineClassCreatorState(new DiagramEditorInlineClassCreatorState(this, point)); } }; this.renderer.onBackgroundDoubleClick = createNewClassView; this.renderer.onAddClassViewClick = createNewClassView; this.renderer.onClassViewRightClick = (classView, point) => { this.setShowContextMenu(true); this.setContextMenuClassView(classView); }; this.renderer.onClassNameDoubleClick = (classView, point) => { if (!this.isReadOnly && !isElementReadOnly(classView.class.value)) { this.setInlineClassRenamerState(new DiagramEditorInlineClassRenamerState(this, classView, point)); } }; const editProperty = (property, point, propertyHolderView) => { if (!this.isReadOnly && !isElementReadOnly(property._OWNER)) { this.setInlinePropertyEditorState(new DiagramEditorInlinePropertyEditorState(this, property, point, Boolean(propertyHolderView))); } }; this.renderer.onClassPropertyDoubleClick = editProperty; this.renderer.handleEditProperty = editProperty; this.renderer.handleAddSimpleProperty = (classView) => { if (!this.isReadOnly && !isElementReadOnly(classView.class.value)) { const _class = classView.class.value; class_addProperty(_class, new Property(`property_${_class.properties.length + 1}`, Multiplicity.ONE, GenericTypeExplicitReference.create(new GenericType(PrimitiveType.STRING)), _class)); this.renderer.render(); } }; } registerCommands() { const DEFAULT_TRIGGER = () => // make sure the current active editor is this diagram editor this.editorStore.tabManagerState.currentTab === this && // make sure the renderer is initialized this.isDiagramRendererInitialized && // since we use hotkeys that can be easily in text input // we would need to do this check to make sure we don't accidentally // trigger hotkeys when the user is typing (!document.activeElement || !['input', 'textarea', 'select'].includes(document.activeElement.tagName.toLowerCase())); this.editorStore.applicationStore.commandService.registerCommand({ key: DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.RECENTER, trigger: this.editorStore.createEditorCommandTrigger(DEFAULT_TRIGGER), action: () => this.renderer.recenter(), }); this.editorStore.applicationStore.commandService.registerCommand({ key: DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.USE_ZOOM_TOOL, trigger: this.editorStore.createEditorCommandTrigger(DEFAULT_TRIGGER), action: () => this.renderer.switchToZoomMode(), }); this.editorStore.applicationStore.commandService.registerCommand({ key: DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.USE_VIEW_TOOL, trigger: this.editorStore.createEditorCommandTrigger(DEFAULT_TRIGGER), action: () => this.renderer.switchToViewMode(), }); this.editorStore.applicationStore.commandService.registerCommand({ key: DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.USE_PAN_TOOL, trigger: this.editorStore.createEditorCommandTrigger(DEFAULT_TRIGGER), action: () => this.renderer.switchToPanMode(), }); this.editorStore.applicationStore.commandService.registerCommand({ key: DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.USE_PROPERTY_TOOL, trigger: this.editorStore.createEditorCommandTrigger(DEFAULT_TRIGGER), action: () => this.renderer.switchToAddPropertyMode(), }); this.editorStore.applicationStore.commandService.registerCommand({ key: DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.USE_INHERITANCE_TOOL, trigger: this.editorStore.createEditorCommandTrigger(DEFAULT_TRIGGER), action: () => this.renderer.switchToAddInheritanceMode(), }); this.editorStore.applicationStore.commandService.registerCommand({ key: DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.ADD_CLASS, trigger: this.editorStore.createEditorCommandTrigger(DEFAULT_TRIGGER), action: () => this.renderer.switchToAddClassMode(), }); this.editorStore.applicationStore.commandService.registerCommand({ key: DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.EJECT_PROPERTY, trigger: this.editorStore.createEditorCommandTrigger(DEFAULT_TRIGGER), action: () => this.renderer.ejectProperty(), }); } deregisterCommands() { [ DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.RECENTER, DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.USE_ZOOM_TOOL, DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.USE_VIEW_TOOL, DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.USE_PAN_TOOL, DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.USE_PROPERTY_TOOL, DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.USE_INHERITANCE_TOOL, DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.ADD_CLASS, DSL_DIAGRAM_LEGEND_STUDIO_COMMAND_KEY.EJECT_PROPERTY, ].forEach((commandKey) => this.editorStore.applicationStore.commandService.deregisterCommand(commandKey)); } reprocess(newElement, editorStore) { const diagramEditorState = new DiagramEditorState(editorStore, newElement); return diagramEditorState; } } //# sourceMappingURL=DiagramEditorState.js.map