UNPKG

synapse-react-client

Version:

[![Build Status](https://travis-ci.com/Sage-Bionetworks/Synapse-React-Client.svg?branch=main)](https://travis-ci.com/Sage-Bionetworks/Synapse-React-Client) [![npm version](https://badge.fury.io/js/synapse-react-client.svg)](https://badge.fury.io/js/synaps

214 lines 15.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EntityFinder = exports.NO_VERSION_NUMBER = void 0; var tslib_1 = require("tslib"); var fontawesome_svg_core_1 = require("@fortawesome/fontawesome-svg-core"); var free_solid_svg_icons_1 = require("@fortawesome/free-solid-svg-icons"); var react_fontawesome_1 = require("@fortawesome/react-fontawesome"); var immutable_1 = require("immutable"); var react_1 = (0, tslib_1.__importStar)(require("react")); var react_bootstrap_1 = require("react-bootstrap"); var react_error_boundary_1 = require("react-error-boundary"); var react_reflex_1 = require("react-reflex"); require("react-reflex/styles.css"); var react_sizeme_1 = require("react-sizeme"); var Arrow_1 = (0, tslib_1.__importDefault)(require("../../assets/icons/Arrow")); var utils_1 = require("../../utils"); var RegularExpressions_1 = require("../../utils/functions/RegularExpressions"); var SynapseContext_1 = require("../../utils/SynapseContext"); var EntityType_1 = require("../../utils/synapseTypes/EntityType"); var ErrorBanner_1 = require("../ErrorBanner"); var Breadcrumbs_1 = require("./Breadcrumbs"); var EntityDetailsList_1 = require("./details/EntityDetailsList"); var SelectionPane_1 = require("./SelectionPane"); var TreeNode_1 = require("./tree/TreeNode"); var TreeView_1 = require("./tree/TreeView"); fontawesome_svg_core_1.library.add(free_solid_svg_icons_1.faTimes, free_solid_svg_icons_1.faSearch); var DEFAULT_SELECTABLE_TYPES = Object.values(EntityType_1.EntityType); var TABLE_DEFAULT_VISIBLE_TYPES = Object.values(EntityType_1.EntityType); var TREE_DEFAULT_VISIBLE_TYPES = [EntityType_1.EntityType.PROJECT, EntityType_1.EntityType.FOLDER]; // In the map used to track selections, we use -1 to denote 'selected without version' // This is necessary because undefined is returned by map.get when the item is not in the map exports.NO_VERSION_NUMBER = -1; var searchForOnlyTypesBooleanQuery = function (entityTypes) { // Boolean query terms will be combined with AND, and there's no way to OR. // So we will negate searching for all omitted types. var allTypes = Object.values(EntityType_1.EntityType); var typesToOmit = allTypes.filter(function (type) { return !entityTypes.includes(type); }); return typesToOmit.map(function (type) { return ({ key: 'node_type', value: type.toString(), not: true, }); }); }; var EntityFinder = function (_a) { var initialScope = _a.initialScope, projectId = _a.projectId, initialContainer = _a.initialContainer, selectMultiple = _a.selectMultiple, onSelectedChange = _a.onSelectedChange, _b = _a.showVersionSelection, showVersionSelection = _b === void 0 ? true : _b, _c = _a.mustSelectVersionNumber, mustSelectVersionNumber = _c === void 0 ? false : _c, _d = _a.selectableTypes, selectableTypes = _d === void 0 ? DEFAULT_SELECTABLE_TYPES : _d, _e = _a.visibleTypesInList, visibleTypesInList = _e === void 0 ? TABLE_DEFAULT_VISIBLE_TYPES : _e, _f = _a.visibleTypesInTree, visibleTypesInTree = _f === void 0 ? TREE_DEFAULT_VISIBLE_TYPES : _f, _g = _a.selectedCopy, selectedCopy = _g === void 0 ? 'Selected' : _g, _h = _a.treeOnly, treeOnly = _h === void 0 ? false : _h; var accessToken = (0, SynapseContext_1.useSynapseContext)().accessToken; var _j = (0, react_1.useState)(false), searchActive = _j[0], setSearchActive = _j[1]; // The raw value of the search input box: var _k = (0, react_1.useState)(), searchInput = _k[0], setSearchInput = _k[1]; // The "parsed" search terms, which are only calculated when Enter is pressed: var _l = (0, react_1.useState)(), searchTerms = _l[0], setSearchTerms = _l[1]; var _m = (0, react_1.useState)({ items: [], }), breadcrumbsProps = _m[0], setBreadcrumbsProps = _m[1]; var _o = (0, react_1.useState)([]), searchByIdResults = _o[0], setSearchByIdResults = _o[1]; var _p = (0, react_1.useState)({ type: EntityDetailsList_1.EntityDetailsListDataConfigurationType.PROMPT, }), configFromTreeView = _p[0], setConfigFromTreeView = _p[1]; var searchInputRef = (0, react_1.useRef)(null); // If a type is selectable, it should be visible in the tree/list (depending on treeOnly) var selectableAndVisibleTypesInTree = (0, react_1.useMemo)(function () { return (0, tslib_1.__spreadArray)((0, tslib_1.__spreadArray)([], visibleTypesInTree, true), selectableTypes, true); }, [visibleTypesInTree, selectableTypes]); var selectableAndVisibleTypesInList = (0, react_1.useMemo)(function () { return (0, tslib_1.__spreadArray)((0, tslib_1.__spreadArray)([], visibleTypesInList, true), selectableTypes, true); }, [visibleTypesInList, selectableTypes]); var handleError = (0, react_error_boundary_1.useErrorHandler)(); var setBreadcrumbs = (0, react_1.useCallback)(function (items) { setBreadcrumbsProps({ items: items, }); }, [setBreadcrumbsProps]); /** * * @param entity The entity to check selected status * @param selected the list of selected entities * @returns a boolean array of length equal to entities.length denoting selection status */ function isSelected(entity, selected) { var match = selected.get(entity.targetId); if (match == null) { return false; } if (match === exports.NO_VERSION_NUMBER) { return entity.targetVersionNumber === undefined; } return match === entity.targetVersionNumber; } /** * Given the existing selections and a list of toggled references, return the new list of selections * @param selected * @param toggledReference * @returns */ function entitySelectionReducer(selected, toggledReferences) { var newSelected = selected.withMutations(function (map) { // Note: we currently don't allow selecting two versions of the same entity, so we replace previous selected version with new selected version if (!Array.isArray(toggledReferences)) { toggledReferences = [toggledReferences]; } toggledReferences.forEach(function (toggledReference) { var _a; if (isSelected(toggledReference, selected)) { // remove from selection map.delete(toggledReference.targetId); } else { // add to selection if (!selectMultiple) { map.clear(); } map.set(toggledReference.targetId, (_a = toggledReference.targetVersionNumber) !== null && _a !== void 0 ? _a : exports.NO_VERSION_NUMBER); } }); }); return newSelected; } var _q = (0, react_1.useReducer)(entitySelectionReducer, (0, immutable_1.Map)()), selectedEntities = _q[0], toggleSelection = _q[1]; (0, react_1.useEffect)(function () { // When it changes, convert the map of selected items to a list of references and invoke the callback onSelectedChange(selectedEntities.toArray().map(function (_a) { var id = _a[0], version = _a[1]; return { targetId: id, targetVersionNumber: version === exports.NO_VERSION_NUMBER ? undefined : version, }; })); }, [selectedEntities, onSelectedChange]); (0, react_1.useEffect)(function () { if ((searchTerms === null || searchTerms === void 0 ? void 0 : searchTerms.length) === 1) { var synIdMatch = RegularExpressions_1.SYNAPSE_ENTITY_ID_REGEX.exec(searchTerms[0]); if (synIdMatch) { utils_1.SynapseClient.getEntityHeaders([ { targetId: synIdMatch[1], targetVersionNumber: synIdMatch[2] ? parseInt(synIdMatch[2]) : undefined, }, ], accessToken) .then(function (response) { setSearchByIdResults(response.results); }) .catch(function (e) { return handleError(e); }); } } else { setSearchByIdResults([]); } }, [accessToken, searchTerms, handleError]); var mainPanelClass = searchActive ? 'MainPanelSearch' : treeOnly ? 'MainPanelSinglePane' : 'MainPanelDualPane'; return (react_1.default.createElement(ErrorBanner_1.SynapseErrorBoundary, null, react_1.default.createElement("div", { className: "bootstrap-4-backport EntityFinder" }, react_1.default.createElement("div", { className: "EntityFinder__Search", "data-active": searchActive }, react_1.default.createElement(react_1.default.Fragment, null, searchActive ? (react_1.default.createElement(react_bootstrap_1.Button, { variant: "gray-primary-500", onClick: function () { setSearchActive(false); setSearchInput(''); setSearchTerms(undefined); } }, react_1.default.createElement(Arrow_1.default, { arrowDirection: "left", style: { height: '18px' } }), "Back to Browse")) : (react_1.default.createElement(react_bootstrap_1.Button, { variant: "gray-primary-500", className: "EntityFinder__Search__SearchButton", onClick: function () { setSearchActive(true); searchInputRef.current.focus(); } }, react_1.default.createElement(react_fontawesome_1.FontAwesomeIcon, { size: 'sm', icon: free_solid_svg_icons_1.faSearch }), "Search all of Synapse")), react_1.default.createElement(react_fontawesome_1.FontAwesomeIcon, { size: 'sm', icon: free_solid_svg_icons_1.faSearch, className: "SearchIcon" }), react_1.default.createElement("input", { role: "textbox", ref: searchInputRef, "aria-hidden": !searchActive, className: "EntityFinder__Search__Input", type: "search", placeholder: "Search by name, Wiki contents, or Synapse ID", value: searchInput, onChange: function (event) { setSearchInput(event.target.value); }, onKeyDown: function (event) { if (event.key === 'Enter') { if (event.target.value === '') { setSearchTerms(undefined); } else { setSearchTerms(event.target.value.trim().split(' ')); } } } }), searchInput && (react_1.default.createElement(react_fontawesome_1.FontAwesomeIcon, { size: 'sm', icon: free_solid_svg_icons_1.faTimes, role: "button", title: "Clear Search", className: "ClearSearchIcon", onClick: function () { setSearchInput(''); setSearchTerms(undefined); } })))), react_1.default.createElement("div", { className: "EntityFinder__MainPanel " + mainPanelClass }, searchActive && (react_1.default.createElement(EntityDetailsList_1.EntityDetailsList, { configuration: searchByIdResults && searchByIdResults.length > 0 ? { type: EntityDetailsList_1.EntityDetailsListDataConfigurationType.HEADER_LIST, headerList: searchByIdResults, } : { type: EntityDetailsList_1.EntityDetailsListDataConfigurationType.ENTITY_SEARCH, query: { queryTerm: searchTerms, booleanQuery: searchForOnlyTypesBooleanQuery(selectableTypes), }, }, showVersionSelection: showVersionSelection, mustSelectVersionNumber: mustSelectVersionNumber, selectColumnType: selectMultiple ? 'checkbox' : 'none', selected: selectedEntities, visibleTypes: selectableTypes, selectableTypes: selectableTypes, toggleSelection: toggleSelection, enableSelectAll: selectMultiple })), react_1.default.createElement("div", { style: searchActive ? { display: 'none' } : {} }, treeOnly ? (react_1.default.createElement("div", null, react_1.default.createElement(TreeView_1.TreeView, { toggleSelection: toggleSelection, showDropdown: true, visibleTypes: selectableAndVisibleTypesInTree, initialScope: initialScope, selectedEntities: selectedEntities, projectId: projectId, initialContainer: initialContainer, showScopeAsRootNode: false, nodeAppearance: TreeNode_1.NodeAppearance.SELECT, selectableTypes: selectableTypes }))) : (react_1.default.createElement("div", { className: "EntityFinderReflexContainer" }, react_1.default.createElement(react_sizeme_1.SizeMe, null, function (_a) { var size = _a.size; return (react_1.default.createElement(react_reflex_1.ReflexContainer, { key: (!!size.width).toString(), orientation: "vertical", windowResizeAware: true }, react_1.default.createElement(react_reflex_1.ReflexElement, { className: "TreeViewReflexElement", flex: 0.18 }, react_1.default.createElement(TreeView_1.TreeView, { selectedEntities: selectedEntities, setDetailsViewConfiguration: setConfigFromTreeView, showDropdown: true, visibleTypes: visibleTypesInTree, initialScope: initialScope, projectId: projectId, initialContainer: initialContainer, nodeAppearance: TreeNode_1.NodeAppearance.BROWSE, setBreadcrumbItems: setBreadcrumbs, selectableTypes: visibleTypesInTree })), react_1.default.createElement(react_reflex_1.ReflexSplitter, null), react_1.default.createElement(react_reflex_1.ReflexElement, { className: "DetailsViewReflexElement" }, react_1.default.createElement(EntityDetailsList_1.EntityDetailsList, { configuration: configFromTreeView, mustSelectVersionNumber: mustSelectVersionNumber, showVersionSelection: showVersionSelection, selected: selectedEntities, visibleTypes: selectableAndVisibleTypesInList, selectableTypes: selectableTypes, selectColumnType: selectMultiple ? 'checkbox' : 'none', toggleSelection: toggleSelection, enableSelectAll: selectMultiple }), react_1.default.createElement(Breadcrumbs_1.Breadcrumbs, (0, tslib_1.__assign)({}, breadcrumbsProps))))); }))))), selectedEntities.size > 0 && (react_1.default.createElement(SelectionPane_1.SelectionPane, { title: selectedCopy, selectedEntities: selectedEntities, toggleSelection: toggleSelection }))))); }; exports.EntityFinder = EntityFinder; exports.default = exports.EntityFinder; //# sourceMappingURL=EntityFinder.js.map