@finos/legend-studio
Version:
166 lines • 15.1 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, } from '@finos/legend-art';
import { MappingEditorState } from '../../../../stores/editor-state/element-editor-state/mapping/MappingEditorState.js';
import { TypeTree } from '../../../shared/TypeTree.js';
import { useDrop } from 'react-dnd';
import { CORE_DND_TYPE, TypeDragSource, } from '../../../../stores/shared/DnDUtil.js';
import { LEGEND_STUDIO_TEST_ID } from '../../../LegendStudioTestID.js';
import { noop } from '@finos/legend-shared';
import { MappingElementDecorator, MappingElementDecorationCleaner, } from '../../../../stores/editor-state/element-editor-state/mapping/MappingElementDecorator.js';
import { useEditorStore } from '../../EditorStoreProvider.js';
import { PRIMITIVE_TYPE, Type, Enum, Enumeration, getEnumValueNames, PackageableElementExplicitReference, } from '@finos/legend-graph';
import { enumMapping_updateSourceType, enumValueMapping_addSourceValue, enumValueMapping_deleteSourceValue, enumValueMapping_updateSourceValue, } from '../../../../stores/graphModifier/DSLMapping_GraphModifierHelper.js';
import { buildElementOption, } from '@finos/legend-application';
const EnumerationMappingSourceSelectorModal = observer((props) => {
const { enumerationMapping, closeModal, open } = props;
const editorStore = useEditorStore();
const options = [
editorStore.graphManagerState.graph.getPrimitiveType(PRIMITIVE_TYPE.INTEGER),
editorStore.graphManagerState.graph.getPrimitiveType(PRIMITIVE_TYPE.STRING),
]
.map(buildElementOption)
.concat(editorStore.enumerationOptions);
const sourceSelectorRef = useRef(null);
const filterOption = createFilter({
ignoreCase: true,
ignoreAccents: false,
stringify: (option) => option.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) {
enumMapping_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("div", { className: "modal search-modal", children: [_jsx("div", { className: "modal__title", children: "Choose a source" }), _jsx(CustomSelectorInput, { ref: sourceSelectorRef, options: options, onChange: changeSourceType, value: selectedSourceType, placeholder: 'Choose a data type or an enumeration...', isClearable: true, filterOption: filterOption })] }) }));
});
export const SourceValueInput = observer((props) => {
const { sourceValue, updateSourceValue, deleteSourceValue, expectedType, isReadOnly, } = props;
const inputType = expectedType?.name === PRIMITIVE_TYPE.INTEGER ? 'number' : 'text';
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) {
updateSourceValue(value + item.data.label);
}
}
}, [isReadOnly, updateSourceValue, value]);
const [{ canDrop }, dropRef] = useDrop(() => ({
accept: CORE_DND_TYPE.TYPE_TREE_ENUM,
drop: (item) => handleDrop(item),
collect: (monitor) => ({
canDrop: monitor.canDrop(),
}),
}), [handleDrop]);
return (_jsxs("div", { ref: dropRef, 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: inputType }), _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.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) {
enumMapping_updateSourceType(enumerationMapping, PackageableElementExplicitReference.create(item.data.packageableElement));
}
}, [enumerationMapping, isReadOnly]);
const [{ isDragOver, canDrop }, dropRef] = 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 (_jsx("div", { "data-testid": LEGEND_STUDIO_TEST_ID.MAIN_EDITOR, className: "editor__main", children: _jsxs("div", { className: "mapping-element-editor 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--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("div", { className: "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("div", { className: "panel__header", 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("div", { className: "panel__header__actions", children: _jsx("button", { className: "panel__header__action", onClick: showSourceSelectorModal, disabled: isReadOnly, tabIndex: -1, title: 'Select Source...', children: _jsx(PencilIcon, {}) }) })] }), _jsxs("div", { ref: dropRef, className: "panel__content dnd__dropzone", children: [sourceType && isDragOver && !isReadOnly && (_jsx("div", { className: "dnd__overlay" })), sourceType && (_jsx("div", { className: "source-panel__explorer", children: sourceType instanceof Enumeration && (_jsx(TypeTree, { type: sourceType })) })), !sourceType && (_jsx(BlankPanelPlaceholder, { placeholderText: "Choose a source", onClick: showSourceSelectorModal, clickActionType: "add", tooltipText: "Drop an enumeration", dndProps: {
isDragOver: isDragOver && !isReadOnly,
canDrop: canDrop && !isReadOnly,
}, readOnlyProps: !isReadOnly
? undefined
: {
placeholderText: 'No source',
} })), _jsx(EnumerationMappingSourceSelectorModal, { enumerationMapping: enumerationMapping, open: openSourceSelectorModal, closeModal: hideSourceSelectorModal })] })] }) })] }) })] }) }));
});
//# sourceMappingURL=EnumerationMappingEditor.js.map