@finos/legend-application-studio
Version:
Legend Studio application core
159 lines • 14.9 kB
JavaScript
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