/** * 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 { useEffect, useState } from 'react'; import { observer } from 'mobx-react-lite'; import { clsx, ControlledDropdownMenu, MenuContent, MenuContentItem, PlusIcon, ArrowsAltHIcon, useResizeDetector, GenericTextFileIcon, } from '@finos/legend-art'; import { MappingEditor } from './mapping-editor/MappingEditor.js'; import { UMLEditor } from './uml-editor/UMLEditor.js'; import { MappingEditorState } from '../../../stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.js'; import { UMLEditorState } from '../../../stores/editor/editor-state/element-editor-state/UMLEditorState.js'; import { ElementEditorState, ELEMENT_GENERATION_MODE, ExternalFormatElementGenerationViewModeState, FileGenerationViewModeState, } from '../../../stores/editor/editor-state/element-editor-state/ElementEditorState.js'; import { LEGEND_STUDIO_TEST_ID } from '../../../__lib__/LegendStudioTesting.js'; import { ELEMENT_NATIVE_VIEW_MODE } from '../../../stores/editor/EditorConfig.js'; import { DIFF_VIEW_MODE, EntityDiffViewState, } from '../../../stores/editor/editor-state/entity-diff-editor-state/EntityDiffViewState.js'; import { EntityDiffView } from './diff-editor/EntityDiffView.js'; import { ModelImporter } from './ModelImporter.js'; import { ModelImporterState } from '../../../stores/editor/editor-state/ModelImporterState.js'; import { FunctionEditorState } from '../../../stores/editor/editor-state/element-editor-state/FunctionEditorState.js'; import { ServiceEditorState } from '../../../stores/editor/editor-state/element-editor-state/service/ServiceEditorState.js'; import { ProjectConfigurationEditorState } from '../../../stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.js'; import { ProjectConfigurationEditor } from './project-configuration-editor/ProjectConfigurationEditor.js'; import { ElementGenerationEditor } from './element-generation-editor/ElementGenerationEditor.js'; import { FunctionEditor } from './function-activator/FunctionEditor.js'; import { ElementNativeView } from './element-generation-editor/ElementNativeView.js'; import { ServiceEditor } from './service-editor/ServiceEditor.js'; import { PackageableRuntimeEditor } from './RuntimeEditor.js'; import { PackageableRuntimeEditorState } from '../../../stores/editor/editor-state/element-editor-state/RuntimeEditorState.js'; import { PackageableConnectionEditorState } from '../../../stores/editor/editor-state/element-editor-state/connection/ConnectionEditorState.js'; import { PackageableConnectionEditor } from './connection-editor/ConnectionEditor.js'; import { FileGenerationEditorState } from '../../../stores/editor/editor-state/element-editor-state/FileGenerationEditorState.js'; import { FileGenerationEditor } from './element-generation-editor/FileGenerationEditor.js'; import { EntityChangeConflictEditorState } from '../../../stores/editor/editor-state/entity-diff-editor-state/EntityChangeConflictEditorState.js'; import { EntityChangeConflictEditor } from './diff-editor/EntityChangeConflictEditor.js'; import { UnsupportedElementEditorState } from '../../../stores/editor/editor-state/UnsupportedElementEditorState.js'; import { UnsupportedElementEditor } from './UnsupportedElementEditor.js'; import { DatabaseEditorState } from '../../../stores/editor/editor-state/element-editor-state/DatabaseEditorState.js'; import { DatabaseEditor } from './database-editor/DatabaseEditor.js'; import { getPrettyLabelForRevision } from '../../../stores/editor/editor-state/entity-diff-editor-state/EntityDiffEditorState.js'; import { GenerationSpecificationEditorState } from '../../../stores/editor/editor-state/GenerationSpecificationEditorState.js'; import { GenerationSpecificationEditor } from './GenerationSpecificationEditor.js'; import { ArtifactGenerationViewer } from './ArtifactGenerationViewer.js'; import type { DSL_LegendStudioApplicationPlugin_Extension } from '../../../stores/LegendStudioApplicationPlugin.js'; import { useEditorStore } from '../EditorStoreProvider.js'; import { PackageableDataEditorState } from '../../../stores/editor/editor-state/element-editor-state/data/DataEditorState.js'; import { DataElementEditor } from './data-editor/DataElementEditor.js'; import { ElementXTGenerationEditor } from './element-generation-editor/ElementXTGenerationEditor.js'; import { TabManager, type TabState } from '@finos/legend-lego/application'; import { INTERNAL__UnknownFunctionActivatorEdtiorState } from '../../../stores/editor/editor-state/element-editor-state/function-activator/INTERNAL__UnknownFunctionActivatorEditorState.js'; import { INTERNAL__UnknownFunctionActivatorEdtior } from './function-activator/INTERNAL__UnknownFunctionActivatorEdtior.js'; import { getElementIcon } from '../../ElementIconUtils.js'; import { ArtifactGenerationViewerState } from '../../../stores/editor/editor-state/ArtifactGenerationViewerState.js'; import { QueryConnectionWorflowEditor } from './end-to-end-flow-editor/ConnectionToQueryWorkflowEditor.js'; import { QueryConnectionEndToEndWorkflowEditorState } from '../../../stores/editor/editor-state/end-to-end-workflow-state/QueryConnectionEndToEndWorkflowEditorState.js'; import { SnowflakeAppFunctionActivatorEdtiorState } from '../../../stores/editor/editor-state/element-editor-state/function-activator/SnowflakeAppFunctionActivatorEditorState.js'; import { SnowflakeAppFunctionActivatorEditor } from './function-activator/SnowflakeAppFunctionActivatorEditor.js'; import { SnowflakeM2MUdfFunctionActivatorEdtiorState } from '../../../stores/editor/editor-state/element-editor-state/function-activator/SnowflakeM2MUdfFunctionActivatorEditorState.js'; import { SnowflakeM2MUdfFunctionActivatorEditor } from './function-activator/SnowflakeM2MUdfFunctionActivatorEditor.js'; import { ShowcaseCard, RuleEngagementCard, DocumentationCard, } from '../../workspace-setup/WorkspaceSetup.js'; import { HostedServiceFunctionActivatorEditorState } from '../../../stores/editor/editor-state/element-editor-state/function-activator/HostedServiceFunctionActivatorEditorState.js'; import { HostedServiceFunctionActivatorEditor } from './function-activator/HostedServiceFunctionActivatorEditor.js'; import { DataProductEditorState } from '../../../stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js'; import { DataProductEditor } from './dataProduct/DataProductEditor.js'; import { IngestDefinitionEditorState } from '../../../stores/editor/editor-state/element-editor-state/ingest/IngestDefinitionEditorState.js'; import { IngestDefinitionEditor } from './ingest-editor/IngestDefinitionEditor.js'; import { AvailabilityEditorState } from '../../../stores/editor/editor-state/element-editor-state/availability/AvailabilityEditorState.js'; import { AvailabilityEditor } from './availability/AvailabilityEditor.js'; import { ComputeEditorState } from '../../../stores/editor/editor-state/element-editor-state/compute/ComputeEditorState.js'; import { ComputeEditor } from './compute-editor/ComputeEditor.js'; import { MemSQLFunctionActivatorEditor, MemSQLFunctionActivatorEdtiorState, } from './EditorGroup.js'; export { MemSQLFunctionActivatorEditorState as MemSQLFunctionActivatorEdtiorState } from '../../../stores/editor/editor-state/element-editor-state/function-activator/MemSQLFunctionActivatorEditorState.js'; export { MemSQLFunctionActivatorEditor } from './function-activator/MemSQLFunctionActivatorEditor.js'; export const ViewerEditorGroupSplashScreen: React.FC = () => { const commandListWidth = 300; const commandListHeight = 50; const [showCommandList, setShowCommandList] = useState(false); const { ref, width, height } = useResizeDetector(); useEffect(() => { setShowCommandList( (width ?? 0) > commandListWidth && (height ?? 0) > commandListHeight, ); }, [width, height]); return (
Open or Search for an Element
Ctrl
P
); }; export const EditorGroupSplashScreen: React.FC = () => { const commandListWidth = 300; const commandListHeight = 180; const [showCommandList, setShowCommandList] = useState(false); const { ref, width, height } = useResizeDetector(); useEffect(() => { setShowCommandList( (width ?? 0) > commandListWidth && (height ?? 0) > commandListHeight, ); }, [width, height]); return (
Essential Keyboard Shortcuts
Open or Search for an Element
Ctrl
P
Push Local Changes
Ctrl
S
Open Showcases
F7
Go To Text Mode
F8
Compile
F9
); }; export const EditorGroup = observer(() => { const editorStore = useEditorStore(); const currentTabState = editorStore.tabManagerState.currentTab; const nativeViewModes = currentTabState instanceof ElementEditorState ? Object.values(ELEMENT_NATIVE_VIEW_MODE) : []; const externalformatViewModes = currentTabState instanceof ElementEditorState ? editorStore.graphState.graphGenerationState.externalFormatState.externalFormatDescriptions .filter((f) => f.supportsSchemaGeneration) .toSorted((a, b): number => a.name.localeCompare(b.name)) : []; const generationViewModes = ( currentTabState instanceof ElementEditorState ? editorStore.graphState.graphGenerationState.globalFileGenerationState.fileGenerationConfigurations.toSorted( (a, b): number => a.label.localeCompare(b.label), ) : [] ).filter( (file) => !externalformatViewModes .map((e) => e.name.toLowerCase()) .includes(file.key.toLowerCase()), ); const renderActiveElementTab = (): React.ReactNode => { if (currentTabState instanceof ElementEditorState) { const generationViewState = currentTabState.generationModeState; if (generationViewState) { if (generationViewState instanceof FileGenerationViewModeState) { return ( ); } else if ( generationViewState instanceof ExternalFormatElementGenerationViewModeState ) { return ( ); } } switch (currentTabState.editMode) { case ELEMENT_NATIVE_VIEW_MODE.FORM: { if (currentTabState instanceof UMLEditorState) { return ; } else if (currentTabState instanceof FunctionEditorState) { return ; } else if (currentTabState instanceof MappingEditorState) { return ; } else if (currentTabState instanceof IngestDefinitionEditorState) { return ; } else if (currentTabState instanceof ServiceEditorState) { return ; } else if (currentTabState instanceof DataProductEditorState) { return ; } else if (currentTabState instanceof AvailabilityEditorState) { return ; } else if (currentTabState instanceof ComputeEditorState) { return ; } else if (currentTabState instanceof PackageableRuntimeEditorState) { return ; } else if ( currentTabState instanceof PackageableConnectionEditorState ) { return ; } else if (currentTabState instanceof FileGenerationEditorState) { return ; } else if (currentTabState instanceof PackageableDataEditorState) { return ; } else if ( currentTabState instanceof GenerationSpecificationEditorState ) { return ; } else if ( currentTabState instanceof SnowflakeAppFunctionActivatorEdtiorState ) { return ( ); } else if ( currentTabState instanceof SnowflakeM2MUdfFunctionActivatorEdtiorState ) { return ( ); } else if ( currentTabState instanceof HostedServiceFunctionActivatorEditorState ) { return ( ); } else if ( currentTabState instanceof MemSQLFunctionActivatorEdtiorState ) { return ; } else if (currentTabState instanceof DatabaseEditorState) { return ; } else if (currentTabState instanceof UnsupportedElementEditorState) { return ; } else if ( currentTabState instanceof INTERNAL__UnknownFunctionActivatorEdtiorState ) { return ( ); } const extraElementEditorCreators = editorStore.pluginManager .getApplicationPlugins() .flatMap( (plugin) => ( plugin as DSL_LegendStudioApplicationPlugin_Extension ).getExtraElementEditorRenderers?.() ?? [], ); for (const elementEditorCreators of extraElementEditorCreators) { const elementEditor = elementEditorCreators(currentTabState); if (elementEditor) { return elementEditor; } } break; } case ELEMENT_NATIVE_VIEW_MODE.JSON: case ELEMENT_NATIVE_VIEW_MODE.GRAMMAR: return ( ); default: return null; } } else if (currentTabState instanceof EntityDiffViewState) { return ( ); } else if (currentTabState instanceof EntityChangeConflictEditorState) { return ( ); } else if (currentTabState instanceof ArtifactGenerationViewerState) { return ; } else if (currentTabState instanceof ModelImporterState) { return ; } else if (currentTabState instanceof ProjectConfigurationEditorState) { return ; } else if ( currentTabState instanceof QueryConnectionEndToEndWorkflowEditorState ) { if ( editorStore.globalEndToEndWorkflowState .queryToConnectionWorkflowEditorState.packageableConnection ?.connectionValue.store ) { return ( ); } else { editorStore.applicationStore.notificationService.notifyError( `Cannot open query to connection workflow editor because the connection does not have a store`, ); return null; } } // TODO: create an editor for unsupported tab return null; }; const renderTab = (editorState: TabState): React.ReactNode | undefined => { if (editorState instanceof EntityDiffViewState) { return (
{editorState.element ? ( (getElementIcon(editorState.element, editorStore, { returnEmptyForUnknown: true, }) ?? ) ) : ( )}
{editorState.label}
({getPrettyLabelForRevision(editorState.fromRevision)}
{getPrettyLabelForRevision(editorState.toRevision)})
); } else if (editorState instanceof EntityChangeConflictEditorState) { return (
{editorState.label}
{editorState.isReadOnly ? '(Merge Preview)' : '(Merged)'}
); } return (
{editorState instanceof ElementEditorState ? ( (getElementIcon(editorState.element, editorStore, { returnEmptyForUnknown: true, }) ?? ) ) : ( )}
{editorState.label}
{editorState instanceof ElementEditorState && editorStore.tabManagerState.tabs.filter( (tab) => tab instanceof ElementEditorState && tab.label === editorState.label, ).length > 1 && (
{editorState.element.path}
)}
); }; if (!currentTabState) { return editorStore.isInViewerMode ? ( ) : ( ); } return (
{currentTabState instanceof ElementEditorState && (
native
{nativeViewModes.map((mode) => ( currentTabState.setEditMode(mode) } > {mode} ))}
{Boolean(externalformatViewModes.length) && ( <>
external format
{externalformatViewModes.map((mode) => ( currentTabState.changeGenerationModeState( mode.name, ELEMENT_GENERATION_MODE.EXTERNAL_FORMAT, ) } > {mode.name} ))}
)} {Boolean(generationViewModes.length) && ( <>
file
{generationViewModes.map((mode) => ( currentTabState.changeGenerationModeState( mode.key, ELEMENT_GENERATION_MODE.FILE_GENERATION, ) } > {mode.label} ))}
)} } menuProps={{ anchorOrigin: { vertical: 'bottom', horizontal: 'right' }, transformOrigin: { vertical: 'top', horizontal: 'right' }, }} >
{currentTabState.generationModeState?.label ?? currentTabState.editMode}
)} {currentTabState instanceof EntityDiffViewState && ( currentTabState.setDiffMode(DIFF_VIEW_MODE.GRAMMAR) } > {DIFF_VIEW_MODE.GRAMMAR} currentTabState.setDiffMode(DIFF_VIEW_MODE.JSON) } > {DIFF_VIEW_MODE.JSON} } menuProps={{ anchorOrigin: { vertical: 'bottom', horizontal: 'right' }, transformOrigin: { vertical: 'top', horizontal: 'right' }, }} >
{currentTabState.diffMode}
)}
{renderActiveElementTab()}
); });