/** * 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 { observer } from 'mobx-react-lite'; import { useEditorStore } from '../../EditorStoreProvider.js'; import type { EmbeddedDataEditorState } from '../../../../stores/editor/editor-state/element-editor-state/data/DataEditorState.js'; import { CaretDownIcon, clsx, CustomSelectorInput, ControlledDropdownMenu, LockIcon, LongArrowRightIcon, MenuContent, MenuContentItem, PanelContent, PURE_ClassIcon, PURE_DataIcon, WrenchIcon, type SelectComponent, createFilter, } from '@finos/legend-art'; import React, { useEffect, useRef } from 'react'; import { UnsupportedEditorPanel } from '../UnsupportedElementEditor.js'; import { externalFormatData_setContentType, externalFormatData_setData, modelStoreData_setDataModelModel, } from '../../../../stores/graph-modifier/DSL_Data_GraphModifierHelper.js'; import type { DSL_Data_LegendStudioApplicationPlugin_Extension } from '../../../../stores/extensions/DSL_Data_LegendStudioApplicationPlugin_Extension.js'; import { type EmbeddedDataState, DataElementReferenceState, ExternalFormatDataState, RelationalCSVDataState, ModelStoreDataState, ModelEmbeddedDataState, RelationElementsDataState, } from '../../../../stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.js'; import { PackageableElementExplicitReference, type Type, type DataElement, } from '@finos/legend-graph'; import { buildElementOption, getPackageableElementOptionFormatter, type PackageableElementOption, } from '@finos/legend-lego/graph-editor'; import { RelationalCSVDataEditor } from './RelationalCSVDataEditor.js'; import { CodeEditor } from '@finos/legend-lego/code-editor'; import { getEditorLanguageForFormat } from '../../../../stores/editor/editor-state/ArtifactGenerationViewerState.js'; import { useApplicationNavigationContext } from '@finos/legend-application'; import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../../../__lib__/LegendStudioApplicationNavigationContext.js'; import { RelationElementsDataEditor } from './RelationElementsDataEditor.js'; export const ExternalFormatDataEditor = observer( (props: { externalFormatDataState: ExternalFormatDataState; isReadOnly: boolean; }) => { const { externalFormatDataState, isReadOnly } = props; const editorStore = useEditorStore(); const typeNameRef = useRef(null); const changeData = (val: string): void => externalFormatData_setData(externalFormatDataState.embeddedData, val); useEffect(() => { if (!isReadOnly) { typeNameRef.current?.focus(); } }, [isReadOnly]); const contentTypeOptions = editorStore.graphState.graphGenerationState.externalFormatState .formatContentTypes; const onContentTypeChange = (val: string): void => externalFormatData_setContentType( externalFormatDataState.embeddedData, val, ); const language = getEditorLanguageForFormat( editorStore.graphState.graphGenerationState.externalFormatState.getFormatTypeForContentType( externalFormatDataState.embeddedData.contentType, ), ); const format = (): void => externalFormatDataState.format(); useApplicationNavigationContext( LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.EMBEDDED_DATA_EXTERNAL_FORMAT_EDITOR, ); return (
{isReadOnly && (
)}
{externalFormatDataState.label()}
{contentTypeOptions.map((contentType) => ( onContentTypeChange(contentType)} > {contentType} ))} } menuProps={{ anchorOrigin: { vertical: 'bottom', horizontal: 'right' }, transformOrigin: { vertical: 'top', horizontal: 'right' }, }} >
{externalFormatDataState.embeddedData.contentType}
); }, ); export const DataElementReferenceDataEditor = observer( (props: { dataElementReferenceState: DataElementReferenceState; isReadOnly: boolean; }) => { const { dataElementReferenceState, isReadOnly } = props; const dataElement = dataElementReferenceState.embeddedData.dataElement.value; const editorStore = dataElementReferenceState.editorStore; const applicationStore = editorStore.applicationStore; const options = editorStore.graphManagerState.usableDataElements.map(buildElementOption); const selectedOption = buildElementOption( dataElement, ) as PackageableElementOption; const onDataElementChange = ( val: PackageableElementOption, ): void => { if (val.value !== selectedOption.value) { dataElementReferenceState.setDataElement(val.value); } }; const visitData = (): void => editorStore.graphEditorMode.openElement(dataElement); useApplicationNavigationContext( LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.EMBEDDED_DATA_DATA_ELEMENT_REFERENCE_EDITOR, ); return (
{isReadOnly && (
)}
{dataElementReferenceState.label()}
{renderEmbeddedDataEditor( dataElementReferenceState.embeddedDataValueState, isReadOnly, true, )}
); }, ); export const ModelEmbeddedDataEditor = observer( (props: { modelDataState: ModelEmbeddedDataState; modelStoreDataState: ModelStoreDataState; isReadOnly: boolean; }) => { const { isReadOnly, modelStoreDataState, modelDataState } = props; const modelData = modelDataState.modelData; const hideClass = modelStoreDataState.hideClass; const classSelectorRef = useRef(null); const elementFilterOption = createFilter({ ignoreCase: true, ignoreAccents: false, stringify: (option: { data: PackageableElementOption }): string => option.data.value.path, }); const editorStore = modelStoreDataState.editorStore; const applicationStore = editorStore.applicationStore; const _class = modelData.model.value; const classOptions = editorStore.graphManagerState.usableClasses.map( (_cl) => ({ value: _cl, label: _cl.name, }), ); const selectedClassOption = { value: _class, label: _class.name, }; const changeClass = (val: PackageableElementOption | null): void => { if (val?.value) { modelStoreData_setDataModelModel( modelData, PackageableElementExplicitReference.create(val.value), ); } }; return ( <> {!hideClass && (
)} {renderEmbeddedDataEditor(modelDataState.embeddedDataState, isReadOnly)} ); }, ); export const ModelStoreDataEditor = observer( (props: { modelStoreDataState: ModelStoreDataState; isReadOnly: boolean; }) => { const { isReadOnly, modelStoreDataState } = props; useApplicationNavigationContext( LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.EMBEDDED_DATA_MODEL_STORE_EDITOR, ); return (
{modelStoreDataState.modelDataStates.map((_modelDataState) => { if (_modelDataState instanceof ModelEmbeddedDataState) { return ( ); } return ( ); })}
); }, ); export const EmbeddedDataEditor = observer( (props: { embeddedDataEditorState: EmbeddedDataEditorState; isReadOnly: boolean; }) => { const { embeddedDataEditorState, isReadOnly } = props; return (
{renderEmbeddedDataEditor( embeddedDataEditorState.embeddedDataState, isReadOnly, )}
); }, ); export function renderEmbeddedDataEditor( embeddedDataState: EmbeddedDataState, isReadOnly: boolean, isSharedData?: boolean, ): React.ReactNode { if (embeddedDataState instanceof ExternalFormatDataState) { return ( ); } else if (embeddedDataState instanceof RelationalCSVDataState) { return ( ); } else if (embeddedDataState instanceof RelationElementsDataState) { return ( ); } else if (embeddedDataState instanceof DataElementReferenceState) { return ( ); } else if (embeddedDataState instanceof ModelStoreDataState) { return ( ); } else { const extraEmbeddedDataEditorRenderers = embeddedDataState.editorStore.pluginManager .getApplicationPlugins() .flatMap( (plugin) => ( plugin as DSL_Data_LegendStudioApplicationPlugin_Extension ).getExtraEmbeddedDataEditorRenderers?.() ?? [], ); for (const editorRenderer of extraEmbeddedDataEditorRenderers) { const editor = editorRenderer(embeddedDataState, isReadOnly); if (editor) { return editor; } } return (
{isReadOnly && (
)}
{embeddedDataState.label()}
); } }