UNPKG

@finos/legend-studio

Version:
398 lines 40.2 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } 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 { useState, useMemo, useCallback, Fragment } from 'react'; import { observer } from 'mobx-react-lite'; import { flowResult, runInAction } from 'mobx'; import { getElementIcon } from '../../../shared/ElementIconUtils.js'; import { useDrop } from 'react-dnd'; import { getTextContent, getEditorLanguageFromFormat, } from '../../../../stores/editor-state/FileGenerationViewerState.js'; import { FileGenerationEditorState } from '../../../../stores/editor-state/element-editor-state/FileGenerationEditorState.js'; import { UnsupportedOperationError, debounce, guaranteeNonNullable, } from '@finos/legend-shared'; import { ResizablePanelGroup, ResizablePanel, ResizablePanelSplitter, ResizablePanelSplitterLine, clsx, TreeView, BlankPanelContent, PanelLoadingIndicator, CustomSelectorInput, PencilIcon, RefreshIcon, TimesIcon, CheckSquareIcon, SquareIcon, ChevronDownIcon, ChevronRightIcon, FolderOpenIcon, FolderIcon, FileCodeIcon, LockIcon, SaveIcon, } from '@finos/legend-art'; import { CORE_DND_TYPE, } from '../../../../stores/shared/DnDUtil.js'; import { GenerationDirectory, GenerationFile, getFileGenerationChildNodes, } from '../../../../stores/shared/FileGenerationTreeUtil.js'; import { LEGEND_STUDIO_TEST_ID } from '../../../LegendStudioTestID.js'; import { useEditorStore } from '../../EditorStoreProvider.js'; import { GenerationPropertyItemType, PackageableElementReference, PackageableElementExplicitReference, isValidFullPath, resolvePackagePathAndElementName, getNullableFileGenerationConfig, } from '@finos/legend-graph'; import { useApplicationStore } from '@finos/legend-application'; import { StudioTextInputEditor } from '../../../shared/StudioTextInputEditor.js'; import { fileGeneration_addScopeElement, fileGeneration_changeScopeElement, fileGeneration_deleteScopeElement, fileGeneration_setGenerationOutputPath, } from '../../../../stores/graphModifier/DSLGeneration_GraphModifierHelper.js'; export const FileGenerationTreeNodeContainer = (props) => { const { node, level, stepPaddingInRem, onNodeSelect, innerProps } = props; const { selectedNode } = innerProps; const isSelected = selectedNode === node; const isDirectory = node.fileNode instanceof GenerationDirectory; const expandIcon = !isDirectory ? (_jsx("div", {})) : node.isOpen ? (_jsx(ChevronDownIcon, {})) : (_jsx(ChevronRightIcon, {})); const iconPackageColor = 'color--generated'; const nodeIcon = isDirectory ? (node.isOpen ? (_jsx("div", { className: iconPackageColor, children: _jsx(FolderOpenIcon, {}) })) : (_jsx("div", { className: iconPackageColor, children: _jsx(FolderIcon, {}) }))) : (_jsx("div", { className: "icon", children: _jsx(FileCodeIcon, {}) })); const selectNode = (event) => onNodeSelect?.(node); return (_jsxs("div", { className: clsx('tree-view__node__container generation-result-viewer__explorer__package-tree__node__container', { 'generation-result-viewer__explorer__package-tree__node__container--selected': isSelected, }), onClick: selectNode, style: { paddingLeft: `${level * (stepPaddingInRem ?? 1)}rem`, display: 'flex', }, children: [_jsxs("div", { className: "tree-view__node__icon generation-result-viewer__explorer__package-tree__node__icon", children: [_jsx("div", { className: "generation-result-viewer__explorer__package-tree__node__icon__expand", children: expandIcon }), _jsx("div", { className: "generation-result-viewer__explorer__package-tree__node__icon__type", children: nodeIcon })] }), _jsx("button", { className: "tree-view__node__label generation-result-viewer__explorer__package-tree__node__label", tabIndex: -1, title: node.fileNode.path, children: node.label })] })); }; export const FileGenerationTree = observer((props) => { const { directoryTreeData, onNodeSelect, getFileElementTreeChildNodes, selectedNode, } = props; return (_jsx(TreeView, { components: { TreeNodeContainer: FileGenerationTreeNodeContainer, }, treeData: directoryTreeData, onNodeSelect: onNodeSelect, getChildNodes: getFileElementTreeChildNodes, innerProps: { selectedNode: selectedNode, } })); }); export const GenerationResultExplorer = observer((props) => { const { fileGenerationState } = props; const treeData = guaranteeNonNullable(fileGenerationState.directoryTreeData); const onNodeSelect = (node) => fileGenerationState.onTreeNodeSelect(node, treeData); const getMappingElementTreeChildNodes = (node) => getFileGenerationChildNodes(node, treeData); if (!treeData.nodes.size) { return _jsx(BlankPanelContent, { children: "No content" }); } return (_jsx("div", { className: "generation-result-viewer__explorer__content", children: _jsx(FileGenerationTree, { selectedNode: fileGenerationState.selectedNode, directoryTreeData: treeData, onNodeSelect: onNodeSelect, getFileElementTreeChildNodes: getMappingElementTreeChildNodes }) })); }); export const GenerationResultViewer = observer((props) => { const { fileGenerationState } = props; const applicationStore = useApplicationStore(); const selectedNode = fileGenerationState.selectedNode; const fileNode = selectedNode?.fileNode; const regenerate = applicationStore.guardUnhandledError(() => flowResult(fileGenerationState.generate())); const extraFileGenerationResultViewerActions = fileNode instanceof GenerationFile ? fileGenerationState.editorStore.pluginManager .getApplicationPlugins() .flatMap((plugin) => plugin.getExtraFileGenerationResultViewerActionConfigurations?.() ?? []) .map((config) => (_jsx(Fragment, { children: config.renderer(fileGenerationState) }, config.key))) : null; return (_jsxs(ResizablePanelGroup, { orientation: "vertical", children: [_jsx(ResizablePanel, { size: 250, minSize: 250, children: _jsx("div", { className: "generation-result-viewer__side-bar", children: _jsxs("div", { className: "panel generation-result-viewer__explorer", children: [_jsxs("div", { className: "panel__header", children: [_jsx("div", { className: "panel__header__title", children: _jsx("div", { className: "panel__header__title__label", children: "result" }) }), _jsx("div", { className: "panel__header__actions", children: _jsx("button", { className: clsx('panel__header__action generation-result-viewer__regenerate-btn', { ' generation-result-viewer__regenerate-btn--loading': fileGenerationState.isGenerating, }), tabIndex: -1, disabled: fileGenerationState.isGenerating, onClick: regenerate, title: 'Re-generate', children: _jsx(RefreshIcon, {}) }) })] }), _jsxs("div", { className: "panel__content", children: [_jsx(PanelLoadingIndicator, { isLoading: fileGenerationState.isGenerating }), Boolean(fileGenerationState.directoryTreeData) && (_jsx(GenerationResultExplorer, { fileGenerationState: fileGenerationState })), Boolean(!fileGenerationState.directoryTreeData) && (_jsx(BlankPanelContent, { children: "Generation result not available" }))] })] }) }) }), _jsx(ResizablePanelSplitter, { children: _jsx(ResizablePanelSplitterLine, { color: "var(--color-dark-grey-200)" }) }), _jsx(ResizablePanel, { children: _jsxs("div", { className: "panel generation-result-viewer__file", children: [_jsxs("div", { className: "panel__header", children: [fileNode && !(fileNode instanceof GenerationDirectory) && (_jsxs("div", { className: "panel__header__title", children: [_jsx("div", { className: "panel__header__title__label", children: "file" }), _jsx("div", { className: "panel__header__title__content generation-result-viewer__file__header__name", children: fileNode.name })] })), _jsx("div", { className: "panel__header__actions", children: extraFileGenerationResultViewerActions })] }), _jsxs("div", { className: "panel__content", children: [fileNode instanceof GenerationFile && (_jsx(StudioTextInputEditor, { inputValue: getTextContent(fileNode.content, fileNode.format), isReadOnly: true, language: getEditorLanguageFromFormat(fileNode.format) })), !(fileNode instanceof GenerationFile) && (_jsx(BlankPanelContent, { children: "No file selected" }))] })] }) })] })); }); const FileGenerationScopeEditor = observer((props) => { const { isReadOnly, fileGenerationState, regenerate } = props; const editorStore = useEditorStore(); const applicationStore = useApplicationStore(); const fileGeneration = fileGenerationState.fileGeneration; const scopeElements = fileGeneration.scopeElements; const scopeElementPath = (element) => element instanceof PackageableElementReference ? element.value.path : element; // NOTE: `showEditInput` is either boolean (to hide/show the add value button) or a number (index of the item being edited) const [showEditInput, setShowEditInput] = useState(false); const [itemValue, setItemValue] = useState(undefined); const hideAddOrEditItemInput = () => { setShowEditInput(false); setItemValue(''); }; const showEditItemInput = (value, idx) => () => { setItemValue(scopeElementPath(value)); setShowEditInput(idx); }; const showAddItemInput = () => { setShowEditInput(true); setItemValue(''); }; const deleteScopeElement = (scopeElement) => { fileGeneration_deleteScopeElement(fileGeneration, scopeElement); regenerate()?.catch(applicationStore.alertUnhandledError); }; const changeItemInputValue = (event) => setItemValue(event.target.value); const isDisabled = Boolean(isReadOnly || !itemValue || scopeElements .map((element) => scopeElementPath(element)) .includes(itemValue)); const addValue = () => { if (itemValue && !isReadOnly) { regenerate.cancel(); const element = editorStore.graphManagerState.graph.getNullableElement(itemValue, true); fileGenerationState.addScopeElement(element ?? itemValue); regenerate()?.catch(applicationStore.alertUnhandledError); hideAddOrEditItemInput(); } }; const updateValue = (value) => () => { if (itemValue && !isReadOnly && !scopeElements .map((element) => scopeElementPath(element)) .includes(itemValue)) { const element = editorStore.graphManagerState.graph.getNullableElement(itemValue, true); if (element) { regenerate.cancel(); fileGeneration_changeScopeElement(fileGeneration, value, PackageableElementExplicitReference.create(element)); regenerate()?.catch(applicationStore.alertUnhandledError); } } hideAddOrEditItemInput(); }; return (_jsxs("div", { className: "panel__content__form__section", children: [_jsx("div", { className: "panel__content__form__section__header__label", children: "Scope" }), _jsx("div", { className: "panel__content__form__section__header__prompt", children: "Specifies the list of packages and elements that will determine elements to get generated" }), _jsxs("div", { className: "panel__content__form__section__list", children: [_jsxs("div", { className: "panel__content__form__section__list__items", "data-testid": LEGEND_STUDIO_TEST_ID.PANEL_CONTENT_FORM_SECTION_LIST_ITEMS, children: [scopeElements.map((value, idx) => ( // NOTE: since the value must be unique, we will use it as the key _jsx("div", { className: showEditInput === idx ? 'panel__content__form__section__list__new-item' : 'panel__content__form__section__list__item', children: showEditInput === idx ? (_jsxs(_Fragment, { children: [_jsx("input", { className: "panel__content__form__section__input panel__content__form__section__list__new-item__input", spellCheck: false, disabled: isReadOnly, value: itemValue, onChange: changeItemInputValue }), _jsxs("div", { className: "panel__content__form__section__list__new-item__actions", children: [_jsx("button", { className: "panel__content__form__section__list__new-item__add-btn btn btn--dark", disabled: isDisabled, onClick: updateValue(value), tabIndex: -1, children: "Save" }), _jsx("button", { className: "panel__content__form__section__list__new-item__cancel-btn btn btn--dark", disabled: isReadOnly, onClick: hideAddOrEditItemInput, tabIndex: -1, children: "Cancel" })] })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "panel__content__form__section__list__item__value file-generation-editor__configuration__content__scope", children: [_jsx("div", { className: "file-generation-editor__configuration__content__scope__icon", children: getElementIcon(editorStore, value instanceof PackageableElementReference ? value.value : undefined) }), _jsx("div", { className: "file-generation-editor__configuration__content__scope__path", children: scopeElementPath(value) })] }), _jsxs("div", { className: "panel__content__form__section__list__item__actions", children: [_jsx("button", { className: "panel__content__form__section__list__item__edit-btn", disabled: isReadOnly, onClick: showEditItemInput(value, idx), tabIndex: -1, children: _jsx(PencilIcon, {}) }), _jsx("button", { className: "panel__content__form__section__list__item__remove-btn", disabled: isReadOnly, onClick: () => deleteScopeElement(value), tabIndex: -1, children: _jsx(TimesIcon, {}) })] })] })) }, scopeElementPath(value)))), showEditInput === true && (_jsxs("div", { className: "panel__content__form__section__list__new-item", children: [_jsx("input", { className: "panel__content__form__section__input panel__content__form__section__list__new-item__input", spellCheck: false, disabled: isReadOnly, value: itemValue, onChange: changeItemInputValue }), _jsxs("div", { className: "panel__content__form__section__list__new-item__actions", children: [_jsx("button", { className: "panel__content__form__section__list__new-item__add-btn btn btn--dark", disabled: isDisabled, onClick: addValue, tabIndex: -1, children: "Save" }), _jsx("button", { className: "panel__content__form__section__list__new-item__cancel-btn btn btn--dark", disabled: isReadOnly, onClick: hideAddOrEditItemInput, tabIndex: -1, children: "Cancel" })] })] }))] }), !scopeElements.length && (_jsx("div", { className: "panel__content__form__section__list__empty", children: "No path specified" })), showEditInput !== true && (_jsx("div", { className: "panel__content__form__section__list__new-item__add", children: _jsx("button", { className: "panel__content__form__section__list__new-item__add-btn btn btn--dark", disabled: isReadOnly, onClick: showAddItemInput, tabIndex: -1, children: "Add Value" }) }))] })] })); }); const GenerationStringPropertyEditor = observer((props) => { const { property, getConfigValue, isReadOnly, update } = props; // If there is no default value the string will be 'null'. We will treat it as an empty string const defaultValue = property.defaultValue === 'null' ? '' : property.defaultValue; const value = getConfigValue(property.name) ?? defaultValue; const changeValue = (event) => update(property, event.target.value); return (_jsxs("div", { className: "panel__content__form__section", children: [_jsx("div", { className: "panel__content__form__section__header__label", children: property.name }), _jsx("div", { className: "panel__content__form__section__header__prompt", children: property.description }), _jsx("input", { className: "panel__content__form__section__input", spellCheck: false, disabled: isReadOnly, value: value, onChange: changeValue })] })); }); const GenerationIntegerPropertyEditor = observer((props) => { const { property, getConfigValue, isReadOnly, update } = props; const defaultValue = JSON.parse(property.defaultValue); const value = getConfigValue(property.name) ?? defaultValue; const changeValue = (event) => update(property, event.target.value); return (_jsxs("div", { className: "panel__content__form__section", children: [_jsx("div", { className: "panel__content__form__section__header__label", children: property.name }), _jsx("div", { className: "panel__content__form__section__header__prompt", children: property.description }), _jsx("input", { className: "panel__content__form__section__input panel__content__form__section__number-input", spellCheck: false, type: "number", disabled: isReadOnly, value: value, onChange: changeValue })] })); }); const GenerationBooleanPropertyEditor = observer((props) => { const { property, getConfigValue, isReadOnly, update } = props; const defaultValue = JSON.parse(property.defaultValue); const value = getConfigValue(property.name) ?? defaultValue; const toggle = () => { if (!isReadOnly) { update(property, !value); } }; return (_jsxs("div", { className: "panel__content__form__section", children: [_jsx("div", { className: "panel__content__form__section__header__label", children: property.name }), _jsxs("div", { className: clsx('panel__content__form__section__toggler', { 'panel__content__form__section__toggler--disabled': isReadOnly, }), onClick: toggle, children: [_jsx("button", { className: clsx('panel__content__form__section__toggler__btn', { 'panel__content__form__section__toggler__btn--toggled': value, }), disabled: isReadOnly, tabIndex: -1, children: value ? _jsx(CheckSquareIcon, {}) : _jsx(SquareIcon, {}) }), _jsx("div", { className: "panel__content__form__section__toggler__prompt", children: property.description })] })] })); }); const GenerationEnumPropertyEditor = observer((props) => { const { property, getConfigValue, isReadOnly, update } = props; const getEnumLabel = (_enum) => isValidFullPath(_enum) ? resolvePackagePathAndElementName(_enum)[1] : _enum; const options = guaranteeNonNullable(property.items, 'Generation configuration description items for enum property item type is missing').enums.map((_enum) => ({ label: getEnumLabel(_enum), value: _enum })); const value = getConfigValue(property.name) ?? property.defaultValue; const onChange = (val) => { if (val !== null && val.value !== value) { update(property, val.value); } }; return (_jsxs("div", { className: "panel__content__form__section", children: [_jsx("div", { className: "panel__content__form__section__header__label", children: property.name }), _jsx("div", { className: "panel__content__form__section__header__prompt", children: property.description }), _jsx(CustomSelectorInput, { className: "panel__content__form__section__dropdown", options: options, onChange: onChange, value: { label: getEnumLabel(value), value }, isClearable: true, escapeClearsValue: true, darkMode: true, disable: isReadOnly })] })); }); const GenerationArrayPropertyEditor = observer((props) => { const { property, getConfigValue, isReadOnly, update } = props; let defaultValue = []; // NOTE: hacking this because the backend send corrupted array string if (property.defaultValue !== '' && property.defaultValue !== '[]') { defaultValue = property.defaultValue .substring(1, property.defaultValue.length - 1) .split(','); } const arrayValues = getConfigValue(property.name) ?? defaultValue; // NOTE: `showEditInput` is either boolean (to hide/show the add value button) or a number (index of the item being edited) const [showEditInput, setShowEditInput] = useState(false); const [itemValue, setItemValue] = useState(''); const showAddItemInput = () => setShowEditInput(true); const showEditItemInput = (value, idx) => () => { setItemValue(value); setShowEditInput(idx); }; const hideAddOrEditItemInput = () => { setShowEditInput(false); setItemValue(''); }; const changeItemInputValue = (event) => setItemValue(event.target.value); const addValue = () => { if (itemValue && !isReadOnly && !arrayValues.includes(itemValue)) { update(property, arrayValues.concat([itemValue])); } hideAddOrEditItemInput(); }; const updateValue = (idx) => () => { if (itemValue && !isReadOnly && !arrayValues.includes(itemValue)) { runInAction(() => { arrayValues[idx] = itemValue; }); update(property, arrayValues); } hideAddOrEditItemInput(); }; const deleteValue = (idx) => () => { if (!isReadOnly) { runInAction(() => arrayValues.splice(idx, 1)); update(property, [...arrayValues]); // Since we keep track of the value currently being edited using the index, we have to account for it as we delete entry if (typeof showEditInput === 'number' && showEditInput > idx) { setShowEditInput(showEditInput - 1); } } }; return (_jsxs("div", { className: "panel__content__form__section", children: [_jsx("div", { className: "panel__content__form__section__header__label", children: property.name }), _jsx("div", { className: "panel__content__form__section__header__prompt", children: property.description }), _jsxs("div", { className: "panel__content__form__section__list", children: [_jsxs("div", { className: "panel__content__form__section__list__items", "data-testid": LEGEND_STUDIO_TEST_ID.PANEL_CONTENT_FORM_SECTION_LIST_ITEMS, children: [arrayValues.map((value, idx) => ( // NOTE: since the value must be unique, we will use it as the key _jsx("div", { className: showEditInput === idx ? 'panel__content__form__section__list__new-item' : 'panel__content__form__section__list__item', children: showEditInput === idx ? (_jsxs(_Fragment, { children: [_jsx("input", { className: "panel__content__form__section__input panel__content__form__section__list__new-item__input", spellCheck: false, disabled: isReadOnly, value: itemValue, onChange: changeItemInputValue }), _jsxs("div", { className: "panel__content__form__section__list__new-item__actions", children: [_jsx("button", { className: "panel__content__form__section__list__new-item__add-btn btn btn--dark", disabled: isReadOnly || arrayValues.includes(itemValue), onClick: updateValue(idx), tabIndex: -1, children: "Save" }), _jsx("button", { className: "panel__content__form__section__list__new-item__cancel-btn btn btn--dark", disabled: isReadOnly, onClick: hideAddOrEditItemInput, tabIndex: -1, children: "Cancel" })] })] })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "panel__content__form__section__list__item__value", children: value }), _jsxs("div", { className: "panel__content__form__section__list__item__actions", children: [_jsx("button", { className: "panel__content__form__section__list__item__edit-btn", disabled: isReadOnly, onClick: showEditItemInput(value, idx), tabIndex: -1, children: _jsx(PencilIcon, {}) }), _jsx("button", { className: "panel__content__form__section__list__item__remove-btn", disabled: isReadOnly, onClick: deleteValue(idx), tabIndex: -1, children: _jsx(TimesIcon, {}) })] })] })) }, value))), showEditInput === true && (_jsxs("div", { className: "panel__content__form__section__list__new-item", children: [_jsx("input", { className: "panel__content__form__section__input panel__content__form__section__list__new-item__input", spellCheck: false, disabled: isReadOnly, value: itemValue, onChange: changeItemInputValue }), _jsxs("div", { className: "panel__content__form__section__list__new-item__actions", children: [_jsx("button", { className: "panel__content__form__section__list__new-item__add-btn btn btn--dark", disabled: isReadOnly || arrayValues.includes(itemValue), onClick: addValue, tabIndex: -1, children: "Save" }), _jsx("button", { className: "panel__content__form__section__list__new-item__cancel-btn btn btn--dark", disabled: isReadOnly, onClick: hideAddOrEditItemInput, tabIndex: -1, children: "Cancel" })] })] }))] }), showEditInput !== true && (_jsx("div", { className: "panel__content__form__section__list__new-item__add", children: _jsx("button", { className: "panel__content__form__section__list__new-item__add-btn btn btn--dark", disabled: isReadOnly, onClick: showAddItemInput, tabIndex: -1, children: "Add Value" }) }))] })] })); }); const GenerationMapPropertyEditor = observer((props) => { const { property, getConfigValue, isReadOnly, update } = props; // Right now, always assume this is a map between STRING and STRING (might need to support STRING - INTEGER and STRING - BOOLEAN) const nonNullableDefaultValue = property.defaultValue === 'null' ? '{}' : property.defaultValue; const mapValues = (getConfigValue(property.name) ?? JSON.parse(nonNullableDefaultValue)); // NOTE: `showEditInput` is either boolean (to hide/show the add value button) or a number (index of the item being edited) const [showEditInput, setShowEditInput] = useState(false); const [itemKey, setItemKey] = useState(''); const [itemValue, setItemValue] = useState(''); const showAddItemInput = () => setShowEditInput(true); const showEditItemInput = (key, value, idx) => () => { setItemKey(key); setItemValue(value); setShowEditInput(idx); }; const hideAddOrEditItemInput = () => { setShowEditInput(false); setItemKey(''); setItemValue(''); }; const changeItemInputKey = (event) => setItemKey(event.target.value); const changeItemInputValue = (event) => setItemValue(event.target.value); const addValue = () => { if (itemValue && itemKey && !isReadOnly && !Array.from(Object.keys(mapValues)).includes(itemKey)) { runInAction(() => { mapValues[itemKey] = itemValue; }); update(property, mapValues); } hideAddOrEditItemInput(); }; const updateValue = (key) => () => { if (itemValue && !isReadOnly) { runInAction(() => { delete mapValues[key]; mapValues[itemKey] = itemValue; }); update(property, mapValues); } hideAddOrEditItemInput(); }; const deleteValue = (key, idx) => () => { if (!isReadOnly) { runInAction(() => delete mapValues[key]); update(property, mapValues); // Since we keep track of the value currently being edited using the index, we have to account for it as we delete entry if (typeof showEditInput === 'number' && showEditInput > idx) { setShowEditInput(showEditInput - 1); } } }; return (_jsxs("div", { className: "panel__content__form__section", children: [_jsx("div", { className: "panel__content__form__section__header__label", children: property.name }), _jsx("div", { className: "panel__content__form__section__header__prompt", children: property.description }), _jsxs("div", { className: "panel__content__form__section__list", children: [_jsxs("div", { className: "panel__content__form__section__list__items", "data-testid": LEGEND_STUDIO_TEST_ID.PANEL_CONTENT_FORM_SECTION_LIST_ITEMS, children: [Array.from(Object.entries(mapValues)).map(([key, value], idx) => ( // NOTE: since the key must be unique, we will use it to generate the key _jsx("div", { className: showEditInput === idx ? 'panel__content__form__section__list__new-item' : 'panel__content__form__section__list__item', children: showEditInput === idx ? (_jsxs(_Fragment, { children: [_jsx("input", { className: "panel__content__form__section__input panel__content__form__section__list__new-item__input", spellCheck: false, disabled: isReadOnly, value: itemKey, onChange: changeItemInputKey }), _jsx("input", { className: "panel__content__form__section__input panel__content__form__section__list__new-item__input", spellCheck: false, disabled: isReadOnly, value: itemValue, onChange: changeItemInputValue }), _jsxs("div", { className: "panel__content__form__section__list__new-item__actions", children: [_jsx("button", { className: "panel__content__form__section__list__new-item__add-btn btn btn--dark", disabled: isReadOnly, onClick: updateValue(key), tabIndex: -1, children: "Save" }), _jsx("button", { className: "panel__content__form__section__list__new-item__cancel-btn btn btn--dark", disabled: isReadOnly, onClick: hideAddOrEditItemInput, tabIndex: -1, children: "Cancel" })] })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "panel__content__form__section__list__item__value panel__content__form__section__list__item__value__map-item", children: [_jsx("span", { className: "panel__content__form__section__list__item__value__map-item__key", children: key }), _jsx("span", { className: "panel__content__form__section__list__item__value__map-item__separator", children: ":" }), _jsx("span", { className: "panel__content__form__section__list__item__value__map-item__value", children: value })] }), _jsxs("div", { className: "panel__content__form__section__list__item__actions", children: [_jsx("button", { className: "panel__content__form__section__list__item__edit-btn", disabled: isReadOnly, onClick: showEditItemInput(key, value, idx), tabIndex: -1, children: _jsx(PencilIcon, {}) }), _jsx("button", { className: "panel__content__form__section__list__item__remove-btn", disabled: isReadOnly, onClick: deleteValue(key, idx), tabIndex: -1, children: _jsx(TimesIcon, {}) })] })] })) }, key))), showEditInput === true && (_jsxs("div", { className: "panel__content__form__section__list__new-item", children: [_jsx("input", { className: "panel__content__form__section__input panel__content__form__section__list__new-item__input", spellCheck: false, disabled: isReadOnly, value: itemKey, onChange: changeItemInputKey }), _jsx("input", { className: "panel__content__form__section__input panel__content__form__section__list__new-item__input", spellCheck: false, disabled: isReadOnly, value: itemValue, onChange: changeItemInputValue }), _jsxs("div", { className: "panel__content__form__section__list__new-item__actions", children: [_jsx("button", { className: "panel__content__form__section__list__new-item__add-btn btn btn--dark", disabled: isReadOnly || Array.from(Object.keys(mapValues)).includes(itemKey), onClick: addValue, tabIndex: -1, children: "Save" }), _jsx("button", { className: "panel__content__form__section__list__new-item__cancel-btn btn btn--dark", disabled: isReadOnly, onClick: hideAddOrEditItemInput, tabIndex: -1, children: "Cancel" })] })] }))] }), showEditInput !== true && (_jsx("div", { className: "panel__content__form__section__list__new-item__add", children: _jsx("button", { className: "panel__content__form__section__list__new-item__add-btn btn btn--dark", disabled: isReadOnly, onClick: showAddItemInput, tabIndex: -1, children: "Add Value" }) }))] })] })); }); export const GenerationPropertyEditor = observer((props) => { const { property, getConfigValue, isReadOnly, update } = props; switch (property.type) { case GenerationPropertyItemType.STRING: return (_jsx(GenerationStringPropertyEditor, { getConfigValue: getConfigValue, isReadOnly: isReadOnly, update: update, property: property })); case GenerationPropertyItemType.INTEGER: return (_jsx(GenerationIntegerPropertyEditor, { getConfigValue: getConfigValue, isReadOnly: isReadOnly, update: update, property: property })); case GenerationPropertyItemType.BOOLEAN: return (_jsx(GenerationBooleanPropertyEditor, { getConfigValue: getConfigValue, isReadOnly: isReadOnly, update: update, property: property })); case GenerationPropertyItemType.ENUM: return (_jsx(GenerationEnumPropertyEditor, { getConfigValue: getConfigValue, isReadOnly: isReadOnly, update: update, property: property })); case GenerationPropertyItemType.ARRAY: return (_jsx(GenerationArrayPropertyEditor, { getConfigValue: getConfigValue, isReadOnly: isReadOnly, update: update, property: property })); case GenerationPropertyItemType.MAP: return (_jsx(GenerationMapPropertyEditor, { getConfigValue: getConfigValue, isReadOnly: isReadOnly, update: update, property: property })); default: throw new UnsupportedOperationError(`Can't generate editor for artifact generation property of type '${property.type}'`); } }); export const FileGenerationConfigurationEditor = observer((props) => { const { isReadOnly, fileGenerationState, elementGenerationState } = props; const editorStore = useEditorStore(); const applicationStore = useApplicationStore(); const fileGeneration = fileGenerationState.fileGeneration; const fileGenerationConfiguration = editorStore.graphState.graphGenerationState.getFileGenerationConfiguration(fileGeneration.type).properties; const debouncedRegenerate = useMemo(() => debounce(() => flowResult(fileGenerationState.generate()), 500), [fileGenerationState]); const update = (generationProperty, newValue) => { debouncedRegenerate.cancel(); fileGenerationState.updateFileGenerationParameters(fileGeneration, generationProperty, newValue); debouncedRegenerate()?.catch(applicationStore.alertUnhandledError); }; const showFileGenerationModal = () => { elementGenerationState?.setShowNewFileGenerationModal(true); }; const resetDefaultConfiguration = () => { debouncedRegenerate.cancel(); fileGenerationState.resetFileGeneration(); debouncedRegenerate()?.catch(applicationStore.alertUnhandledError); }; const changeValue = (event) => { const val = event.target.value; fileGeneration_setGenerationOutputPath(fileGeneration, val || undefined); }; // Drag and Drop const handleDrop = useCallback((item) => { const element = item.data.packageableElement; if (!isReadOnly && !elementGenerationState && !fileGenerationState.getScopeElement(element)) { debouncedRegenerate.cancel(); fileGeneration_addScopeElement(fileGeneration, PackageableElementExplicitReference.create(element)); debouncedRegenerate()?.catch(applicationStore.alertUnhandledError); } }, [ applicationStore.alertUnhandledError, debouncedRegenerate, elementGenerationState, fileGeneration, fileGenerationState, isReadOnly, ]); const [{ isScopeElementDragOver }, scopeElementDropRef] = useDrop(() => ({ accept: [ CORE_DND_TYPE.PROJECT_EXPLORER_PACKAGE, CORE_DND_TYPE.PROJECT_EXPLORER_CLASS, CORE_DND_TYPE.PROJECT_EXPLORER_ENUMERATION, ], drop: (item) => handleDrop(item), collect: (monitor) => ({ isScopeElementDragOver: monitor.isOver({ shallow: true }), }), }), [handleDrop]); const getConfigValue = (name) => getNullableFileGenerationConfig(fileGeneration, name)?.value; return (_jsxs("div", { className: "panel file-generation-editor__configuration", children: [_jsxs("div", { className: "panel__header", children: [_jsx("div", { className: "panel__header__title", children: _jsx("div", { className: "panel__header__title__label", children: `${fileGeneration.type} configuration` }) }), _jsxs("div", { className: "panel__header__actions", children: [_jsx("button", { className: "panel__header__action file-generation-editor__configuration__reset-btn", tabIndex: -1, disabled: isReadOnly || !fileGeneration.configurationProperties.length, onClick: resetDefaultConfiguration, title: 'Reset to default configuration', children: _jsx(RefreshIcon, {}) }), Boolean(elementGenerationState) && (_jsx("button", { className: "panel__header__action", tabIndex: -1, disabled: isReadOnly, onClick: showFileGenerationModal, title: 'Promote File Generation Specification...', children: _jsx(SaveIcon, {}) }))] })] }), _jsxs("div", { ref: scopeElementDropRef, className: "panel__content dnd__dropzone", children: [_jsx("div", { className: clsx({ dnd__overlay: isScopeElementDragOver && !elementGenerationState && !isReadOnly, }) }), _jsxs("div", { className: "file-generation-editor__configuration__content", children: [_jsx(FileGenerationScopeEditor, { fileGenerationState: fileGenerationState, regenerate: debouncedRegenerate, isReadOnly: isReadOnly || Boolean(elementGenerationState) }), _jsxs("div", { className: "panel__content__form__section", children: [_jsx("div", { className: "panel__content__form__section__header__label", children: "Generation Output Path" }), _jsx("div", { className: "panel__content__form__section__header__prompt", children: "Specifies the root path where files will be generated. Defaults to the file specification path" }), _jsx("input", { className: "panel__content__form__section__input", spellCheck: false, disabled: isReadOnly, value: fileGeneration.generationOutputPath ?? '', onChange: changeValue })] }), fileGenerationConfiguration.map((abstractGenerationProperty) => (_jsx(GenerationPropertyEditor, { update: update, isReadOnly: isReadOnly, getConfigValue: getConfigValue, property: abstractGenerationProperty }, abstractGenerationProperty.name + abstractGenerationProperty.type)))] })] })] })); }); export const FileGenerationEditor = observer(() => { const editorStore = useEditorStore(); const fileGenerationEditorState = editorStore.getCurrentEditorState(FileGenerationEditorState); const fileGeneration = fileGenerationEditorState.fileGeneration; const isReadOnly = fileGenerationEditorState.isReadOnly; return (_jsx("div", { className: "file-generation-editor", children: _jsxs("div", { className: "panel", children: [_jsx("div", { className: "panel__header", children: _jsxs("div", { className: "panel__header__title", children: [isReadOnly && (_jsx("div", { className: "uml-element-editor__header__lock", children: _jsx(LockIcon, {}) })), _jsx("div", { className: "panel__header__title__label", children: "file generation" }), _jsx("div", { className: "panel__header__title__content", children: fileGeneration.name })] }) }), _jsx("div", { className: "panel__content file-generation-editor__content", children: _jsxs(ResizablePanelGroup, { orientation: "vertical", children: [_jsx(ResizablePanel, { size: 400, minSize: 300, children: _jsx(FileGenerationConfigurationEditor, { isReadOnly: isReadOnly, fileGenerationState: fileGenerationEditorState.fileGenerationState }) }), _jsx(ResizablePanelSplitter, { children: _jsx(ResizablePanelSplitterLine, { color: "var(--color-dark-grey-200)" }) }), _jsx(ResizablePanel, { children: _jsx(GenerationResultViewer, { fileGenerationState: fileGenerationEditorState.fileGenerationState }) })] }) })] }) })); }); //# sourceMappingURL=FileGenerationEditor.js.map