synapse-react-client
Version:
[](https://travis-ci.com/Sage-Bionetworks/Synapse-React-Client) [](https://badge.fury.io/js/synaps
214 lines • 15.2 kB
JavaScript
"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