UNPKG

@finos/legend-application-studio

Version:
159 lines 14.9 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 { useRef, useState, useEffect, useCallback } from 'react'; import { observer } from 'mobx-react-lite'; import { Dialog, ResizablePanel, ResizablePanelGroup, ResizablePanelSplitter, clsx, CustomSelectorInput, BlankPanelPlaceholder, createFilter, PURE_EnumerationIcon, PencilIcon, TimesIcon, PlusIcon, PanelDropZone, Panel, PanelContent, ModalTitle, Modal, PanelHeaderActionItem, PanelHeaderActions, PanelHeader, } from '@finos/legend-art'; import { MappingEditorState } from '../../../../stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.js'; import { TypeTree } from './TypeTree.js'; import { useDrop } from 'react-dnd'; import { CORE_DND_TYPE, TypeDragSource, } from '../../../../stores/editor/utils/DnDUtils.js'; import { LEGEND_STUDIO_TEST_ID } from '../../../../__lib__/LegendStudioTesting.js'; import { noop } from '@finos/legend-shared'; import { MappingElementDecorator, MappingElementDecorationCleaner, } from '../../../../stores/editor/editor-state/element-editor-state/mapping/MappingElementDecorator.js'; import { useEditorStore } from '../../EditorStoreProvider.js'; import { Type, Enum, Enumeration, getEnumValueNames, PackageableElementExplicitReference, PrimitiveType, } from '@finos/legend-graph'; import { enumerationMapping_updateSourceType, enumValueMapping_addSourceValue, enumValueMapping_deleteSourceValue, enumValueMapping_updateSourceValue, } from '../../../../stores/graph-modifier/DSL_Mapping_GraphModifierHelper.js'; import { buildElementOption, getPackageableElementOptionFormatter, } from '@finos/legend-lego/graph-editor'; const EnumerationMappingSourceSelectorModal = observer((props) => { const { enumerationMapping, closeModal, open } = props; const editorStore = useEditorStore(); const applicationStore = editorStore.applicationStore; const options = [PrimitiveType.INTEGER, PrimitiveType.STRING] .map(buildElementOption) .concat(editorStore.graphManagerState.usableEnumerations.map(buildElementOption)); const sourceSelectorRef = useRef(null); const filterOption = createFilter({ ignoreCase: true, ignoreAccents: false, stringify: (option) => option.data.value.path, }); const sourceType = enumerationMapping.sourceType?.value; const selectedSourceType = sourceType ? { value: sourceType, label: sourceType.name } : null; const changeSourceType = (val) => { const value = val?.value; if (!value || value instanceof Type) { enumerationMapping_updateSourceType(enumerationMapping, value ? PackageableElementExplicitReference.create(value) : undefined); } if (value) { closeModal(); } }; const handleEnter = () => sourceSelectorRef.current?.focus(); return (_jsx(Dialog, { open: open, onClose: closeModal, TransitionProps: { onEnter: handleEnter, }, classes: { container: 'search-modal__container', }, PaperProps: { classes: { root: 'search-modal__inner-container', }, }, children: _jsxs(Modal, { className: "search-modal", darkMode: !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled, children: [_jsx(ModalTitle, { title: "Choose a Source" }), _jsx(CustomSelectorInput, { inputRef: sourceSelectorRef, options: options, onChange: changeSourceType, value: selectedSourceType, placeholder: "Choose a type...", isClearable: true, darkMode: !applicationStore.layoutService .TEMPORARY__isLightColorThemeEnabled, filterOption: filterOption, formatOptionLabel: getPackageableElementOptionFormatter({}) })] }) })); }); export const SourceValueInput = observer((props) => { const { sourceValue, updateSourceValue, deleteSourceValue, expectedType, isReadOnly, } = props; const value = sourceValue.value instanceof Enum ? sourceValue.value.name : (sourceValue.value ?? ''); const onChange = (event) => updateSourceValue(event.target.value); const onBlur = (event) => { const val = event.target.value; // Small validation if sourceType is enumeration // reset if not an enum value set to enum if source value not an enum if (expectedType instanceof Enumeration && !getEnumValueNames(expectedType).includes(val)) { updateSourceValue(undefined); } else if (expectedType instanceof Enumeration && !(sourceValue instanceof Enum) && getEnumValueNames(expectedType).includes(val)) { const enumValue = expectedType.values.find((e) => e.name === val); updateSourceValue(enumValue); } }; // Drag and Drop const handleDrop = useCallback((item) => { if (!isReadOnly) { if (item instanceof TypeDragSource && item.data) { updateSourceValue(value + item.data.label); } } }, [isReadOnly, updateSourceValue, value]); const [{ canDrop }, dropConnector] = useDrop(() => ({ accept: CORE_DND_TYPE.TYPE_TREE_ENUM, drop: (item) => handleDrop(item), collect: (monitor) => ({ canDrop: monitor.canDrop(), }), }), [handleDrop]); const ref = useRef(null); dropConnector(ref); return (_jsxs("div", { ref: ref, className: clsx('enumeration-mapping-editor__enum-value__source-value', { 'enumeration-mapping-editor__enum-value__source-value--dnd-match': canDrop && !isReadOnly, }), children: [_jsx("input", { className: "enumeration-mapping-editor__enum-value__source-value__input", disabled: isReadOnly, spellCheck: false, value: value, onChange: onChange, onBlur: onBlur, placeholder: "Source value", type: expectedType === PrimitiveType.INTEGER ? 'number' : 'text' }), _jsx("div", { className: "enumeration-mapping-editor__enum-value__source-value__info", children: _jsx("div", { className: "enumeration-mapping-editor__enum-value__source-value__expected-return-type", children: expectedType?.name ?? 'unknown' }) }), !isReadOnly && (_jsx("button", { className: "btn--icon enumeration-mapping-editor__enum-value__source-value__remove-btn", disabled: isReadOnly, onClick: deleteSourceValue, tabIndex: -1, title: "Remove", children: _jsx(TimesIcon, {}) }))] })); }); const EnumValueMappingEditor = observer((props) => { const { enumValue, enumerationMapping, sourceType, isReadOnly } = props; const matchingEnumValueMapping = enumerationMapping.enumValueMappings.find((evm) => evm.enum.value === enumValue); const addSourceValue = () => matchingEnumValueMapping ? enumValueMapping_addSourceValue(matchingEnumValueMapping, undefined) : undefined; return (_jsxs("div", { className: "enumeration-mapping-editor__enum-value", children: [_jsxs("div", { className: "enumeration-mapping-editor__enum-value__metadata", children: [_jsx("div", { className: "enumeration-mapping-editor__enum-value__name", children: enumValue.name }), matchingEnumValueMapping && (_jsx("button", { className: "enumeration-mapping-editor__enum-value__add-btn", disabled: isReadOnly, onClick: addSourceValue, tabIndex: -1, title: "Add enum value", children: _jsx(PlusIcon, {}) }))] }), matchingEnumValueMapping && (_jsxs("div", { children: [matchingEnumValueMapping.sourceValues.map((sourceValue, idx) => (_jsx(SourceValueInput, { isReadOnly: isReadOnly, sourceValue: sourceValue, expectedType: sourceType?.value, updateSourceValue: (val) => enumValueMapping_updateSourceValue(matchingEnumValueMapping, idx, val, sourceType?.value), deleteSourceValue: () => enumValueMapping_deleteSourceValue(matchingEnumValueMapping, idx) }, sourceValue._UUID))), !matchingEnumValueMapping.sourceValues.length && (_jsxs("div", { className: "enumeration-mapping-editor__enum-value__source-value--empty", children: ["No source value. Click", ' ', _jsx("div", { className: "enumeration-mapping-editor__enum-value__source-value--empty__add-btn", onClick: addSourceValue, children: _jsx(PlusIcon, {}) }), ' ', "to add one."] }))] }))] })); }); export const EnumerationMappingEditor = observer((props) => { const { enumerationMapping, isReadOnly } = props; const editorStore = useEditorStore(); const mappingEditorState = editorStore.tabManagerState.getCurrentEditorState(MappingEditorState); const enumeration = enumerationMapping.enumeration; // ID const showId = Object.keys(mappingEditorState.mappingElementsWithSimilarTarget).length > 1; // Source const sourceType = enumerationMapping.sourceType?.value; const [openSourceSelectorModal, setOpenSourceSelectorModal] = useState(false); const showSourceSelectorModal = () => isReadOnly ? undefined : setOpenSourceSelectorModal(true); const hideSourceSelectorModal = () => setOpenSourceSelectorModal(false); const handleDrop = useCallback((item) => { if (!isReadOnly && item.data.packageableElement instanceof Type) { enumerationMapping_updateSourceType(enumerationMapping, PackageableElementExplicitReference.create(item.data.packageableElement)); } }, [enumerationMapping, isReadOnly]); const [{ isDragOver, canDrop }, dropConnector] = useDrop(() => ({ accept: CORE_DND_TYPE.PROJECT_EXPLORER_ENUMERATION, drop: (item) => handleDrop(item), collect: (monitor) => ({ isDragOver: monitor.isOver({ shallow: true }), canDrop: monitor.canDrop(), }), }), [handleDrop]); useEffect(() => { if (!isReadOnly) { new MappingElementDecorator(editorStore).visitEnumerationMapping(enumerationMapping); } return isReadOnly ? noop() : () => new MappingElementDecorationCleaner(editorStore).visitEnumerationMapping(enumerationMapping); }, [enumerationMapping, isReadOnly, editorStore]); return (_jsxs("div", { className: "mapping-element-editor enumeration-mapping-editor", "data-testid": LEGEND_STUDIO_TEST_ID.ENUMERATION_MAPPING_EDITOR, children: [_jsxs("div", { className: "mapping-element-editor__metadata", children: [_jsxs("div", { className: "mapping-element-editor__metadata__chunk mapping-element-editor__metadata__overview-chunk background--enumeration", children: [_jsx("div", { className: "mapping-element-editor__metadata__sub-chunk", children: "enumeration mapping" }), showId && (_jsx("div", { className: "mapping-element-editor__metadata__sub-chunk mapping-element-editor__metadata__overview__id", children: enumerationMapping.id.isDefault ? 'default ID' : enumerationMapping.id.value })), _jsx("div", { className: "mapping-element-editor__metadata__sub-chunk", children: "for" }), _jsxs("div", { className: "mapping-element-editor__metadata__sub-chunk mapping-element-editor__metadata__target", children: [_jsx("div", { className: "mapping-element-editor__metadata__target__type icon", children: _jsx(PURE_EnumerationIcon, {}) }), _jsx("div", { className: "mapping-element-editor__metadata__target__label", children: enumeration.value.name })] })] }), sourceType && (_jsxs("div", { className: clsx('mapping-element-editor__metadata__chunk', 'mapping-element-editor__metadata__driver-chunk', 'background--primitive', 'mapping-element-editor__metadata__source-chunk--primitive', { 'mapping-element-editor__metadata__source-chunk--none': !sourceType, }), children: [_jsx("div", { className: "mapping-element-editor__metadata__sub-chunk", children: "using" }), _jsx("div", { className: "mapping-element-editor__metadata__sub-chunk mapping-element-editor__metadata__driver__type", children: sourceType.name })] })), !sourceType && (_jsx("div", { className: clsx('mapping-element-editor__metadata__chunk', 'mapping-element-editor__metadata__source-chunk', 'mapping-element-editor__metadata__source-chunk--none'), children: _jsx("div", { className: "mapping-element-editor__metadata__sub-chunk", children: "with no source" }) }))] }), _jsx("div", { className: "mapping-element-editor__content", children: _jsxs(ResizablePanelGroup, { orientation: "vertical", children: [_jsx(ResizablePanel, { minSize: 300, children: _jsxs(Panel, { children: [_jsx("div", { className: "panel__header", children: _jsx("div", { className: "panel__header__title", children: _jsx("div", { className: "panel__header__title__content", children: "ENUMS" }) }) }), _jsx("div", { className: "panel__content enumeration-mapping-editor__enum-values", children: enumeration.value.values.map((enumValue) => (_jsx(EnumValueMappingEditor, { enumValue: enumValue, enumerationMapping: enumerationMapping, sourceType: enumerationMapping.sourceType, isReadOnly: isReadOnly }, enumValue.name))) })] }) }), _jsx(ResizablePanelSplitter, {}), _jsx(ResizablePanel, { size: 300, minSize: 300, children: _jsxs("div", { "data-testid": LEGEND_STUDIO_TEST_ID.SOURCE_PANEL, className: "panel source-panel", children: [_jsxs(PanelHeader, { children: [_jsxs("div", { className: "panel__header__title", children: [_jsx("div", { className: "panel__header__title__label", children: "source" }), _jsx("div", { className: "panel__header__title__content", children: sourceType?.name ?? '(none)' })] }), _jsx(PanelHeaderActions, { children: _jsx(PanelHeaderActionItem, { onClick: showSourceSelectorModal, disabled: isReadOnly, title: "Choose a source...", children: _jsx(PencilIcon, {}) }) })] }), _jsx(PanelContent, { children: _jsxs(PanelDropZone, { dropTargetConnector: dropConnector, isDragOver: Boolean(sourceType) && isDragOver && !isReadOnly, children: [sourceType && (_jsx("div", { className: "source-panel__explorer", children: sourceType instanceof Enumeration && (_jsx(TypeTree, { type: sourceType })) })), !sourceType && (_jsx(BlankPanelPlaceholder, { text: "Choose a source", onClick: showSourceSelectorModal, clickActionType: "add", tooltipText: "Drop an enumeration", isDropZoneActive: canDrop, disabled: isReadOnly, previewText: "No source" })), _jsx(EnumerationMappingSourceSelectorModal, { enumerationMapping: enumerationMapping, open: openSourceSelectorModal, closeModal: hideSourceSelectorModal })] }) })] }) })] }) })] })); }); //# sourceMappingURL=EnumerationMappingEditor.js.map