UNPKG

@finos/legend-extension-dsl-data-space-studio

Version:
170 lines 18.8 kB
import { jsx as _jsx, jsxs as _jsxs } 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 { CORE_DND_TYPE, useEditorStore, } from '@finos/legend-application-studio'; import { observer } from 'mobx-react-lite'; import { useDrop } from 'react-dnd'; import { DataSpaceEditorState } from '../stores/DataSpaceEditorState.js'; import { BlankPanelPlaceholder, clsx, ContextMenu, CustomSelectorInput, Dialog, ErrorIcon, ExclamationTriangleIcon, InputWithInlineValidation, LongArrowRightIcon, MenuContent, MenuContentItem, ModalTitle, PanelContent, PanelDropZone, PanelHeader, PanelHeaderActionItem, PanelHeaderActions, PlusIcon, PURE_MappingIcon, PURE_RuntimeIcon, ResizablePanel, ResizablePanelGroup, ResizablePanelSplitter, ResizablePanelSplitterLine, } from '@finos/legend-art'; import { Mapping, PackageableElementExplicitReference, PackageableRuntime, validate_PureExecutionMapping, } from '@finos/legend-graph'; import { dataSpace_setExecutionContextDefaultRuntime, dataSpace_setExecutionContextMapping, dataSpace_setExecutionContextTitle, dataSpace_setExecutionContextDescription, } from '../stores/studio/DSL_DataSpace_GraphModifierHelper.js'; import { guaranteeNonNullable } from '@finos/legend-shared'; import { forwardRef, useCallback, useState } from 'react'; import { buildElementOption, } from '@finos/legend-lego/graph-editor'; const DataSpaceExecutionContextConfigurationEditor = observer((props) => { const { executionContextState, executionContext } = props; const isReadOnly = executionContextState.dataSpaceEditorState.isReadOnly; const editorStore = executionContextState.editorStore; const applicationStore = editorStore.applicationStore; // Mapping // TODO: this is not generic error handling, as there could be other problems // with mapping, we need to genericize this const isMappingEmpty = validate_PureExecutionMapping(executionContext.mapping.value); const mapping = executionContext.mapping.value; const mappingOptions = editorStore.graphManagerState.usableMappings.map(buildElementOption); const noMappingLabel = (_jsxs("div", { className: "service-execution-editor__configuration__mapping-option--empty", title: isMappingEmpty?.messages.join('\n') ?? '', children: [_jsx("div", { className: "service-execution-editor__configuration__mapping-option--empty__label", children: "(none)" }), _jsx(ErrorIcon, {})] })); const selectedMappingOption = { value: mapping, label: isMappingEmpty ? noMappingLabel : mapping.path, }; const onMappingSelectionChange = (val) => { if (val.value !== mapping) { dataSpace_setExecutionContextMapping(executionContext, PackageableElementExplicitReference.create(val.value)); executionContextState.autoSelectRuntimeOnMappingChange(val.value); } }; const visitMapping = () => editorStore.graphEditorMode.openElement(mapping); // Runtime const defaultRuntime = executionContext.defaultRuntime; // NOTE: for now, only include runtime associated with the mapping // TODO?: Should we bring the runtime compatibility check from query to here? const runtimes = editorStore.graphManagerState.graph.runtimes.filter((rt) => rt.runtimeValue.mappings.map((m) => m.value).includes(mapping)); const runtimeOptions = runtimes.map((rt) => ({ label: rt.path, value: PackageableElementExplicitReference.create(rt), })); const runtimePointerWarning = !runtimes.includes(defaultRuntime.value) // if the runtime does not belong to the chosen mapping ? `runtime is not associated with specified mapping '${mapping.path}'` : undefined; const selectedRuntimeOption = { value: defaultRuntime, label: (_jsxs("div", { className: "service-execution-editor__configuration__runtime-option__pointer", title: undefined, children: [_jsx("div", { className: clsx('service-execution-editor__configuration__runtime-option__pointer__label', { 'service-execution-editor__configuration__runtime-option__pointer__label--with-warning': Boolean(runtimePointerWarning), }), children: defaultRuntime.value.path }), runtimePointerWarning && (_jsx("div", { className: "service-execution-editor__configuration__runtime-option__pointer__warning", title: runtimePointerWarning, children: _jsx(ExclamationTriangleIcon, {}) }))] })), }; const onRuntimeSelectionChange = (val) => { if (val.value?.value !== defaultRuntime.value && val.value !== undefined) { dataSpace_setExecutionContextDefaultRuntime(executionContext, val.value); } }; const visitRuntime = () => { editorStore.graphEditorMode.openElement(defaultRuntime.value); }; // DnD const handleMappingOrRuntimeDrop = useCallback((item) => { const element = item.data.packageableElement; if (!isReadOnly) { if (element instanceof Mapping) { dataSpace_setExecutionContextMapping(executionContext, PackageableElementExplicitReference.create(element)); executionContextState.autoSelectRuntimeOnMappingChange(element); } else if (element instanceof PackageableRuntime && element.runtimeValue.mappings.map((m) => m.value).includes(mapping)) { dataSpace_setExecutionContextDefaultRuntime(executionContext, PackageableElementExplicitReference.create(element)); } } }, [isReadOnly, mapping, executionContextState, executionContext]); const [{ isMappingOrRuntimeDragOver }, dropConnector] = useDrop(() => ({ accept: [ CORE_DND_TYPE.PROJECT_EXPLORER_MAPPING, CORE_DND_TYPE.PROJECT_EXPLORER_RUNTIME, ], drop: (item) => handleMappingOrRuntimeDrop(item), collect: (monitor) => ({ isMappingOrRuntimeDragOver: monitor.isOver({ shallow: true }), }), }), [handleMappingOrRuntimeDrop]); return (_jsx(PanelContent, { children: _jsx(PanelDropZone, { dropTargetConnector: dropConnector, isDragOver: isMappingOrRuntimeDragOver && !isReadOnly, children: _jsxs("div", { className: "service-execution-editor__configuration__items", children: [_jsxs("div", { className: "service-execution-editor__configuration__item", children: [_jsx("div", { className: "btn--sm service-execution-editor__configuration__item__label", children: _jsx(PURE_MappingIcon, {}) }), _jsx(CustomSelectorInput, { className: "panel__content__form__section__dropdown service-execution-editor__configuration__item__dropdown", disabled: isReadOnly, options: mappingOptions, onChange: onMappingSelectionChange, value: selectedMappingOption, darkMode: !applicationStore.layoutService .TEMPORARY__isLightColorThemeEnabled, hasError: Boolean(isMappingEmpty) }), _jsx("button", { className: "btn--dark btn--sm service-execution-editor__configuration__item__btn", onClick: visitMapping, tabIndex: -1, title: "See mapping", disabled: Boolean(isMappingEmpty), children: _jsx(LongArrowRightIcon, {}) })] }), _jsxs("div", { className: "service-execution-editor__configuration__item", children: [_jsx("div", { className: "btn--sm service-execution-editor__configuration__item__label", children: _jsx(PURE_RuntimeIcon, {}) }), _jsx(CustomSelectorInput, { className: "panel__content__form__section__dropdown service-execution-editor__configuration__item__dropdown", disabled: isReadOnly, options: runtimeOptions, onChange: onRuntimeSelectionChange, value: selectedRuntimeOption, darkMode: !applicationStore.layoutService .TEMPORARY__isLightColorThemeEnabled }), _jsx("button", { className: "btn--sm btn--dark service-execution-editor__configuration__item__btn", onClick: visitRuntime, tabIndex: -1, title: "See runtime", children: _jsx(LongArrowRightIcon, {}) })] }), _jsxs("div", { className: "execution-context-editor__form-section", children: [_jsx("div", { className: "execution-context-editor__form-section__label", children: "Title" }), _jsx("div", { className: "execution-context-editor__form-section__content", children: _jsx("input", { className: "execution-context-editor__form-section__content__input panel__content__form__section__input", spellCheck: false, disabled: isReadOnly, value: executionContext.title ?? '', onChange: (event) => dataSpace_setExecutionContextTitle(executionContext, event.target.value) }) })] }), _jsxs("div", { className: "execution-context-editor__form-section", children: [_jsx("div", { className: "execution-context-editor__form-section__label", children: "Description" }), _jsx("div", { className: "execution-context-editor__form-section__content", children: _jsx("textarea", { className: "execution-context-editor__form-section__content__textarea panel__content__form__section__input", spellCheck: false, disabled: isReadOnly, value: executionContext.description ?? '', onChange: (event) => dataSpace_setExecutionContextDescription(executionContext, event.target.value), rows: 4 }) })] })] }) }) })); }); export const NewExecutionContextModal = observer((props) => { const { executionState, isReadOnly } = props; const [name, setName] = useState(''); const validationMessage = name === '' ? `Execution context name can't be empty` : executionState.executionContexts.find((e) => e.name === name) ? 'Execution context name already exists' : undefined; const closeModal = () => executionState.setNewExecutionContextModal(false); const onChange = (event) => { setName(event.target.value); }; return (_jsx(Dialog, { open: executionState.newExecutionContextModal, onClose: closeModal, classes: { container: 'search-modal__container' }, PaperProps: { classes: { root: 'search-modal__inner-container' } }, children: _jsxs("form", { onSubmit: (event) => { event.preventDefault(); executionState.addExecutionContext(name); setName(''); closeModal(); }, className: "modal modal--dark search-modal", children: [_jsx(ModalTitle, { title: "New Execution Context" }), _jsx("div", { className: "service-execution-editor__change__modal", children: _jsx(InputWithInlineValidation, { className: "service-execution-editor__input input-group__input", spellCheck: false, value: name, onChange: onChange, placeholder: "Key execution name", error: validationMessage }) }), _jsx("div", { className: "search-modal__actions", children: _jsx("button", { className: "btn btn--dark", disabled: isReadOnly || Boolean(validationMessage), children: "Add" }) })] }) })); }); export const RenameModal = observer((props) => { const { val, isReadOnly, showModal, closeModal, setValue, executionContext, } = props; const [inputValue, setInputValue] = useState(val); const changeValue = (event) => { setInputValue(event.target.value); }; return (_jsx(Dialog, { open: showModal, onClose: closeModal, classes: { container: 'search-modal__container' }, PaperProps: { classes: { root: 'search-modal__inner-container' } }, children: _jsxs("form", { onSubmit: (event) => { event.preventDefault(); setValue(inputValue); closeModal(); }, className: "modal modal--dark search-modal", children: [_jsx(ModalTitle, { title: "Edit Execution Context" }), _jsxs("div", { className: "execution-context-editor__form-section", children: [_jsx("div", { className: "execution-context-editor__form-section__label", children: "Key" }), _jsx("div", { className: "execution-context-editor__form-section__content", children: _jsx("input", { className: "execution-context-editor__form-section__content__input panel__content__form__section__input", spellCheck: false, disabled: isReadOnly, value: inputValue, onChange: changeValue }) })] }), _jsxs("div", { className: "execution-context-editor__form-section", children: [_jsx("div", { className: "execution-context-editor__form-section__label", children: "Title" }), _jsx("div", { className: "execution-context-editor__form-section__content", children: _jsx("input", { className: "execution-context-editor__form-section__content__input panel__content__form__section__input", spellCheck: false, disabled: isReadOnly, value: executionContext.title ?? '', onChange: (event) => dataSpace_setExecutionContextTitle(executionContext, event.target.value) }) })] }), _jsxs("div", { className: "execution-context-editor__form-section", children: [_jsx("div", { className: "execution-context-editor__form-section__label", children: "Description" }), _jsx("div", { className: "execution-context-editor__form-section__content", children: _jsx("textarea", { className: "execution-context-editor__form-section__content__textarea panel__content__form__section__input", spellCheck: false, disabled: isReadOnly, value: executionContext.description ?? '', onChange: (event) => dataSpace_setExecutionContextDescription(executionContext, event.target.value), rows: 4 }) })] }), _jsx("div", { className: "search-modal__actions", children: _jsx("button", { className: "btn btn--dark", disabled: isReadOnly, children: "Done" }) })] }) })); }); const ExecutionContextMenu = observer(forwardRef(function TestContainerContextMenu(props, ref) { const { dataSpaceExecutionContextState, dataSpaceExecutionContext } = props; const rename = () => { dataSpaceExecutionContextState.setExecutionContextToRename(dataSpaceExecutionContext); }; const remove = () => { dataSpaceExecutionContextState.removeExecutionContext(dataSpaceExecutionContext); }; const add = () => { dataSpaceExecutionContextState.setNewExecutionContextModal(true); }; return (_jsxs(MenuContent, { ref: ref, children: [_jsx(MenuContentItem, { onClick: rename, children: "Rename" }), _jsx(MenuContentItem, { onClick: remove, children: "Delete" }), _jsx(MenuContentItem, { onClick: add, children: "Create a new key" })] })); })); const ExecutionContextItem = observer((props) => { const { dataSpaceExecutionContextState, dataSpaceExecutionContext, isReadOnly, } = props; const [isSelectedFromContextMenu, setIsSelectedFromContextMenu] = useState(false); const isActive = dataSpaceExecutionContextState.selectedExecutionContext === dataSpaceExecutionContext; const openKeyedExecution = () => dataSpaceExecutionContextState.setSelectedExecutionContext(dataSpaceExecutionContext); const onContextMenuOpen = () => setIsSelectedFromContextMenu(true); const onContextMenuClose = () => setIsSelectedFromContextMenu(false); return (_jsx(ContextMenu, { className: clsx('service-multi-execution-editor__item', { 'service-multi-execution-editor__item--selected-from-context-menu': !isActive && isSelectedFromContextMenu, }, { 'service-multi-execution-editor__item--active': isActive }), disabled: isReadOnly, content: _jsx(ExecutionContextMenu, { dataSpaceExecutionContextState: dataSpaceExecutionContextState, dataSpaceExecutionContext: dataSpaceExecutionContext, isReadOnly: isReadOnly }), menuProps: { elevation: 7 }, onOpen: onContextMenuOpen, onClose: onContextMenuClose, children: _jsx("button", { className: clsx('service-multi-execution-editor__item__label'), onClick: openKeyedExecution, tabIndex: -1, children: dataSpaceExecutionContext.name }) })); }); export const DataSpaceExecutionContextEditor = observer(() => { const editorStore = useEditorStore(); const dataSpaceState = editorStore.tabManagerState.getCurrentEditorState(DataSpaceEditorState); const executionContextState = dataSpaceState.executionContextState; const addExecutionKey = () => { executionContextState.setNewExecutionContextModal(true); }; return (_jsx("div", { className: "service-execution-editor__execution", children: _jsxs(ResizablePanelGroup, { orientation: "vertical", children: [_jsxs(ResizablePanel, { size: 300, minSize: 200, children: [_jsxs("div", { className: "service-multi-execution-editor__panel", children: [_jsxs(PanelHeader, { children: [_jsx("div", { className: "panel__header__title", children: _jsx("div", { className: "panel__header__title__content", children: "Execution Contexts" }) }), _jsx(PanelHeaderActions, { children: _jsx(PanelHeaderActionItem, { disabled: dataSpaceState.isReadOnly, onClick: addExecutionKey, title: "Add an execution context", children: _jsx(PlusIcon, {}) }) })] }), executionContextState.executionContexts.map((executionContext) => (_jsx(ExecutionContextItem, { dataSpaceExecutionContextState: executionContextState, dataSpaceExecutionContext: executionContext, isReadOnly: dataSpaceState.isReadOnly }, executionContext.name))), !executionContextState.executionContexts.length && (_jsx(BlankPanelPlaceholder, { text: "Add an execution context", onClick: addExecutionKey, clickActionType: "add", tooltipText: "Click to add an execution context" }))] }), executionContextState.newExecutionContextModal && (_jsx(NewExecutionContextModal, { executionState: executionContextState, isReadOnly: dataSpaceState.isReadOnly })), executionContextState.executionContextToRename && (_jsx(RenameModal, { val: executionContextState.executionContextToRename.name, isReadOnly: dataSpaceState.isReadOnly, showModal: true, closeModal: () => executionContextState.setExecutionContextToRename(undefined), setValue: (val) => executionContextState.renameExecutionContext(guaranteeNonNullable(executionContextState.executionContextToRename), val), executionContext: executionContextState.executionContextToRename }))] }), _jsx(ResizablePanelSplitter, { children: _jsx(ResizablePanelSplitterLine, { color: "var(--color-dark-grey-200)" }) }), _jsx(ResizablePanel, { minSize: 56, children: executionContextState.selectedExecutionContext ? (_jsx(DataSpaceExecutionContextConfigurationEditor, { executionContextState: executionContextState, executionContext: executionContextState.selectedExecutionContext })) : (_jsx(BlankPanelPlaceholder, { text: "Add an execution context", onClick: addExecutionKey, clickActionType: "add", tooltipText: "Click to add an execution context" })) })] }) })); }); //# sourceMappingURL=DataSpaceExecutionContextEditor.js.map