@finos/legend-application-studio
Version:
Legend Studio application core
194 lines • 20.3 kB
JavaScript
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 { useApplicationStore } from '@finos/legend-application';
import { observer } from 'mobx-react-lite';
import { BlankPanelContent, Panel, PanelLoadingIndicator, TreeView, ChevronDownIcon, ChevronRightIcon, GenericTextFileIcon, FolderIcon, FolderOpenIcon, HomeIcon, SearchIcon, TimesIcon, CodeIcon, clsx, CopyIcon, compareLabelFn, } from '@finos/legend-art';
import { SHOWCASE_MANAGER_SEARCH_CATEGORY, SHOWCASE_MANAGER_VIEW, ShowcaseManagerState, } from '../stores/ShowcaseManagerState.js';
import { debounce, isNonNullable } from '@finos/legend-shared';
import { flowResult } from 'mobx';
import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor';
import { CodeEditor } from '@finos/legend-lego/code-editor';
import React, { useEffect, useMemo, useRef } from 'react';
import { generateShowcasePath } from '../__lib__/LegendStudioNavigation.js';
const ShowcasesExplorerTreeNodeContainer = observer((props) => {
const { node, level, innerProps } = props;
const { toggleExpandNode, showcaseManagerState } = innerProps;
const applicationStore = useApplicationStore();
const expandIcon = !node.metadata ? (node.isOpen ? (_jsx(ChevronDownIcon, {})) : (_jsx(ChevronRightIcon, {}))) : (_jsx("div", {}));
const nodeIcon = !node.metadata ? (node.isOpen ? (_jsx(FolderOpenIcon, {})) : (_jsx(FolderIcon, {}))) : (_jsx(GenericTextFileIcon, {}));
const onNodeClick = () => {
if (!node.metadata) {
toggleExpandNode(node);
}
else {
flowResult(showcaseManagerState.openShowcase(node.metadata)).catch(applicationStore.alertUnhandledError);
}
};
return (_jsxs("div", { className: "tree-view__node__container showcase-manager__explorer__node__container", style: {
paddingLeft: `${(level - 1) * 1.4}rem`,
display: 'flex',
}, onClick: onNodeClick, children: [_jsx("div", { className: "showcase-manager__explorer__node__expand-icon", children: expandIcon }), _jsx("div", { className: "showcase-manager__explorer__node__type-icon", children: nodeIcon }), _jsx("div", { className: "tree-view__node__label showcase-manager__explorer__node__label", title: node.metadata
? `${node.metadata.description
? `${node.metadata.description}\n\n`
: ''}Click to open showcase`
: undefined, children: node.label })] }));
});
const ShowcaseManagerExplorer = observer((props) => {
const { showcaseManagerState } = props;
const treeData = showcaseManagerState.explorerTreeData;
const getChildNodes = (node) => {
if (treeData) {
return node.childrenIds
.map((id) => treeData.nodes.get(id))
.filter(isNonNullable)
.sort(compareLabelFn);
}
return [];
};
const toggleExpandNode = (node) => {
if (treeData) {
node.isOpen = !node.isOpen;
showcaseManagerState.setExplorerTreeData({ ...treeData });
}
};
return (_jsxs("div", { className: "showcase-manager__view", children: [_jsxs("div", { className: "showcase-manager__view__header", children: [_jsxs("div", { className: "showcase-manager__view__breadcrumbs", children: [_jsxs("div", { className: "showcase-manager__view__breadcrumb", children: [_jsx("div", { className: "showcase-manager__view__breadcrumb__icon", children: _jsx(HomeIcon, {}) }), _jsx("div", { className: "showcase-manager__view__breadcrumb__text", children: "Showcases" })] }), _jsx("div", { className: "showcase-manager__view__breadcrumb__arrow", children: _jsx(ChevronRightIcon, {}) }), _jsx("div", { className: "showcase-manager__view__breadcrumb", children: _jsx("div", { className: "showcase-manager__view__breadcrumb__text", children: "Explorer" }) })] }), _jsx("button", { className: "showcase-manager__view__search-action", tabIndex: -1, title: "Search", onClick: () => {
showcaseManagerState.closeShowcase();
showcaseManagerState.setCurrentView(SHOWCASE_MANAGER_VIEW.SEARCH);
}, children: _jsx(SearchIcon, {}) })] }), _jsx("div", { className: "showcase-manager__view__content", children: _jsx("div", { className: "showcase-manager__explorer", children: showcaseManagerState.explorerTreeData && (_jsx(TreeView, { components: {
TreeNodeContainer: ShowcasesExplorerTreeNodeContainer,
}, treeData: showcaseManagerState.explorerTreeData, getChildNodes: getChildNodes, innerProps: {
toggleExpandNode,
showcaseManagerState,
} })) }) })] }));
});
const renderPreviewLine = (line, text, result) => {
const lineMatches = result.match.matches
.filter((match) => match.line === line)
.sort((a, b) => a.startColumn - b.startColumn);
const chunks = [];
let currentIdx = 0;
lineMatches.forEach((match, idx) => {
if (currentIdx < match.startColumn - 1) {
chunks.push(text.substring(currentIdx, match.startColumn - 1));
}
chunks.push(_jsx("span", { className: "showcase-manager__search__code-result__content__line__text--highlighted", children: text.substring(match.startColumn - 1, match.endColumn - 1) }));
currentIdx = match.endColumn - 1;
});
if (currentIdx < text.length) {
chunks.push(text.substring(currentIdx, text.length));
}
return (_jsx(_Fragment, { children: chunks.map((chunk, idx) => (
// eslint-disable-next-line react/no-array-index-key
_jsx(React.Fragment, { children: chunk }, idx))) }));
};
const ShowcaseManagerCodeSearchResult = observer((props) => {
const { showcaseManagerState, result } = props;
const applicationStore = useApplicationStore();
return (_jsxs("div", { className: "showcase-manager__search__code-result", children: [_jsxs("div", { className: "showcase-manager__search__code-result__header", title: `Showcase: ${result.showcase.title}\n\n${result.showcase.description ?? '(no description)'}\n\nClick to open showcase`, onClick: () => {
flowResult(showcaseManagerState.openShowcase(result.showcase)).catch(applicationStore.alertUnhandledError);
}, children: [_jsx("div", { className: "showcase-manager__search__code-result__header__icon", children: _jsx(GenericTextFileIcon, {}) }), _jsx("div", { className: "showcase-manager__search__code-result__header__title", children: result.showcase.path })] }), _jsx("div", { className: "showcase-manager__search__code-result__content", children: result.match.preview.map((entry) => (_jsxs("div", { className: "showcase-manager__search__code-result__content__line", onClick: () => {
flowResult(showcaseManagerState.openShowcase(result.showcase, entry.line)).catch(applicationStore.alertUnhandledError);
}, children: [_jsx("div", { className: clsx('showcase-manager__search__code-result__content__line__gutter', {
'showcase-manager__search__code-result__content__line__gutter--highlighted': Boolean(result.match.matches.find((match) => match.line === entry.line)),
}), children: entry.line }), _jsx("div", { className: "showcase-manager__search__code-result__content__line__text", children: renderPreviewLine(entry.line, entry.text, result) })] }, entry.line))) })] }));
});
const ShowcaseManagerSearchPanel = observer((props) => {
const { showcaseManagerState } = props;
const applicationStore = useApplicationStore();
const searchTextInpurRef = useRef(null);
const debouncedSearch = useMemo(() => debounce(() => flowResult(showcaseManagerState.search()).catch(applicationStore.alertUnhandledError), 300), [applicationStore, showcaseManagerState]);
const clearSearchText = () => {
debouncedSearch.cancel();
showcaseManagerState.resetSearch();
};
const onSearchTextChange = (event) => {
const value = event.target.value;
showcaseManagerState.setSearchText(value);
debouncedSearch.cancel();
if (!value) {
showcaseManagerState.resetSearch();
}
else {
debouncedSearch()?.catch(applicationStore.alertUnhandledError);
}
};
const onSearchKeyDown = (event) => {
if (event.code === 'Enter') {
debouncedSearch.cancel();
if (showcaseManagerState.searchText) {
debouncedSearch()?.catch(applicationStore.alertUnhandledError);
}
}
else if (event.code === 'Escape') {
searchTextInpurRef.current?.select();
}
};
useEffect(() => {
searchTextInpurRef.current?.select();
searchTextInpurRef.current?.focus();
}, []);
return (_jsxs("div", { className: "showcase-manager__view", children: [_jsxs("div", { className: "showcase-manager__view__header", children: [_jsxs("div", { className: "showcase-manager__view__breadcrumbs", children: [_jsxs("div", { className: "showcase-manager__view__breadcrumb", onClick: () => {
showcaseManagerState.closeShowcase();
showcaseManagerState.setCurrentView(SHOWCASE_MANAGER_VIEW.EXPLORER);
}, children: [_jsx("div", { className: "showcase-manager__view__breadcrumb__icon", children: _jsx(HomeIcon, {}) }), _jsx("div", { className: "showcase-manager__view__breadcrumb__text", children: "Showcases" })] }), _jsx("div", { className: "showcase-manager__view__breadcrumb__arrow", children: _jsx(ChevronRightIcon, {}) }), _jsx("div", { className: "showcase-manager__view__breadcrumb", children: _jsx("div", { className: "showcase-manager__view__breadcrumb__text", children: "Search" }) })] }), _jsx("button", { className: "showcase-manager__view__search-action", tabIndex: -1, title: "Search", onClick: () => {
// since we're already on the search tab, clicking this will just focus the search input
searchTextInpurRef.current?.select();
}, children: _jsx(SearchIcon, {}) })] }), _jsxs("div", { className: "showcase-manager__view__content", children: [_jsxs("div", { className: "showcase-manager__search__header", children: [_jsx("input", { ref: searchTextInpurRef, className: "showcase-manager__search__input input--dark", spellCheck: false, placeholder: "Search for showcase, code, etc.", value: showcaseManagerState.searchText, onChange: onSearchTextChange, onKeyDown: onSearchKeyDown }), !showcaseManagerState.searchText ? (_jsx("div", { className: "showcase-manager__search__input__search__icon", children: _jsx(SearchIcon, {}) })) : (_jsx("button", { className: "showcase-manager__search__input__clear-btn", tabIndex: -1, onClick: clearSearchText, title: "Clear", children: _jsx(TimesIcon, {}) }))] }), _jsxs("div", { className: "showcase-manager__search__results", children: [_jsxs("div", { className: "showcase-manager__search__results__categories", children: [_jsxs("div", { className: clsx('showcase-manager__search__results__category', {
'showcase-manager__search__results__category--active': showcaseManagerState.currentSearchCaterogy ===
SHOWCASE_MANAGER_SEARCH_CATEGORY.SHOWCASE,
}), onClick: () => showcaseManagerState.setCurrentSearchCategory(SHOWCASE_MANAGER_SEARCH_CATEGORY.SHOWCASE), title: "Click to select category", children: [_jsxs("div", { className: "showcase-manager__search__results__category__content", children: [_jsx("div", { className: "showcase-manager__search__results__category__icon", children: _jsx(GenericTextFileIcon, {}) }), _jsx("div", { className: "showcase-manager__search__results__category__label", children: "Showcases" })] }), _jsx("div", { className: "showcase-manager__search__results__category__counter", children: showcaseManagerState.showcaseSearchResults?.length ?? 0 })] }), _jsxs("div", { className: clsx('showcase-manager__search__results__category', {
'showcase-manager__search__results__category--active': showcaseManagerState.currentSearchCaterogy ===
SHOWCASE_MANAGER_SEARCH_CATEGORY.CODE,
}), onClick: () => showcaseManagerState.setCurrentSearchCategory(SHOWCASE_MANAGER_SEARCH_CATEGORY.CODE), title: "Click to select category", children: [_jsxs("div", { className: "showcase-manager__search__results__category__content", children: [_jsx("div", { className: "showcase-manager__search__results__category__icon", children: _jsx(CodeIcon, {}) }), _jsx("div", { className: "showcase-manager__search__results__category__label", children: "Code" })] }), _jsx("div", { className: "showcase-manager__search__results__category__counter", children: showcaseManagerState.textSearchResults?.length ?? 0 })] })] }), _jsxs("div", { className: "showcase-manager__search__results__list", children: [showcaseManagerState.currentSearchCaterogy ===
SHOWCASE_MANAGER_SEARCH_CATEGORY.SHOWCASE && (_jsxs(_Fragment, { children: [!showcaseManagerState.showcaseSearchResults?.length && (_jsx(BlankPanelContent, { children: "No results" })), showcaseManagerState.showcaseSearchResults?.map((showcase) => (_jsxs("div", { className: "showcase-manager__search__showcase-result", title: `Showcase: ${showcase.title}\n\n${showcase.description ?? '(no description)'}\n\nClick to open showcase`, onClick: () => {
flowResult(showcaseManagerState.openShowcase(showcase)).catch(applicationStore.alertUnhandledError);
}, children: [_jsx("div", { className: "showcase-manager__search__showcase-result__title", children: showcase.title }), _jsx("div", { className: "showcase-manager__search__showcase-result__description", children: showcase.description ?? '(no description)' })] }, showcase.uuid)))] })), showcaseManagerState.currentSearchCaterogy ===
SHOWCASE_MANAGER_SEARCH_CATEGORY.CODE && (_jsxs(_Fragment, { children: [!showcaseManagerState.textSearchResults?.length && (_jsx(BlankPanelContent, { children: "No results" })), showcaseManagerState.textSearchResults?.map((result) => (_jsx(ShowcaseManagerCodeSearchResult, { showcaseManagerState: showcaseManagerState, result: result }, result.showcase.uuid)))] }))] })] })] })] }));
});
const ShowcaseViewer = observer((props) => {
const { showcaseManagerState, showcase } = props;
const prettyPath = showcase.path.replaceAll(/\s*\/\s*/g, ' / ');
const launchShowcase = () => {
const applicationStore = showcaseManagerState.applicationStore;
applicationStore.navigationService.navigator.visitAddress(applicationStore.navigationService.navigator.generateAddress(generateShowcasePath(showcase.path)));
};
const handleCopy = showcaseManagerState.applicationStore.guardUnhandledError(() => showcaseManagerState.applicationStore.clipboardService.copyTextToClipboard(showcase.code, {
notifySuccessMessage: 'Showcase grammar copied to clipboard',
}));
return (_jsxs("div", { className: "showcase-manager__view", children: [_jsxs("div", { className: "showcase-manager__view__header", children: [_jsxs("div", { className: "showcase-manager__view__breadcrumbs", children: [_jsxs("div", { className: "showcase-manager__view__breadcrumb", onClick: () => {
showcaseManagerState.closeShowcase();
showcaseManagerState.setCurrentView(SHOWCASE_MANAGER_VIEW.EXPLORER);
}, children: [_jsx("div", { className: "showcase-manager__view__breadcrumb__icon", children: _jsx(HomeIcon, {}) }), _jsx("div", { className: "showcase-manager__view__breadcrumb__text", children: "Showcases" })] }), _jsx("div", { className: "showcase-manager__view__breadcrumb__arrow", children: _jsx(ChevronRightIcon, {}) }), _jsxs("div", { className: "showcase-manager__view__breadcrumb", children: [_jsx("div", { className: "showcase-manager__view__breadcrumb__icon", children: _jsx(GenericTextFileIcon, {}) }), _jsx("div", { className: "showcase-manager__view__breadcrumb__text", children: showcase.title })] })] }), _jsx("button", { className: "showcase-manager__view__search-action", tabIndex: -1, title: "Search", onClick: () => {
showcaseManagerState.closeShowcase();
showcaseManagerState.setCurrentView(SHOWCASE_MANAGER_VIEW.SEARCH);
}, children: _jsx(SearchIcon, {}) })] }), _jsxs("div", { className: "showcase-manager__view__content showcase-manager__viewer__content", children: [_jsxs("div", { className: "showcase-manager__viewer__title", children: [_jsx("div", { className: "showcase-manager__viewer__title__label", children: showcase.title }), _jsx("div", { className: "showcase-manager__viewer__title__action", children: _jsx("div", { className: "btn__dropdown-combo btn__dropdown-combo--primary showcase-manager__viewer__title__action-btn", children: _jsx("button", { className: "btn__dropdown-combo__label", onClick: launchShowcase, title: "Open Showcase Project", tabIndex: -1, children: _jsx("div", { className: "btn__dropdown-combo__label__title", children: "Launch" }) }) }) }), _jsx("div", { className: "showcase-manager__viewer__title__action", children: _jsx("div", { onClick: handleCopy, className: "showcase-manager__viewer__title__action-icon", children: _jsx(CopyIcon, {}) }) })] }), _jsx("div", { className: "showcase-manager__viewer__path", children: prettyPath }), _jsx("div", { className: "showcase-manager__viewer__code", children: _jsx(CodeEditor, { language: CODE_EDITOR_LANGUAGE.PURE, inputValue: showcase.code, isReadOnly: true, lineToScroll: showcaseManagerState.showcaseLineToScroll }) })] })] }));
});
const ShowcaseManagerContent = observer((props) => {
const { showcaseManagerState } = props;
const currentShowcase = showcaseManagerState.currentShowcase;
const currentView = showcaseManagerState.currentView;
return (_jsxs("div", { className: "showcase-manager", children: [currentShowcase && (_jsx(ShowcaseViewer, { showcaseManagerState: showcaseManagerState, showcase: currentShowcase })), !currentShowcase && (_jsxs(_Fragment, { children: [currentView === SHOWCASE_MANAGER_VIEW.EXPLORER && (_jsx(ShowcaseManagerExplorer, { showcaseManagerState: showcaseManagerState })), currentView === SHOWCASE_MANAGER_VIEW.SEARCH && (_jsx(ShowcaseManagerSearchPanel, { showcaseManagerState: showcaseManagerState }))] }))] }));
});
export const ShowcaseManager = observer(() => {
const applicationStore = useApplicationStore();
const showcaseManagerState = ShowcaseManagerState.retrieveNullableState(applicationStore);
if (!showcaseManagerState) {
return null;
}
return (_jsxs(Panel, { children: [_jsx(PanelLoadingIndicator, { isLoading: showcaseManagerState.initState.isInProgress }), showcaseManagerState.initState.isInProgress && (_jsx(BlankPanelContent, { children: "Initializing..." })), showcaseManagerState.initState.hasFailed && (_jsx(BlankPanelContent, { children: "Failed to initialize" })), showcaseManagerState.initState.hasSucceeded && (_jsx(ShowcaseManagerContent, { showcaseManagerState: showcaseManagerState }))] }));
});
//# sourceMappingURL=ShowcaseManager.js.map