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

298 lines 21.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FacetPlotLegend = exports.getPlotStyle = exports.extractPlotDataArray = exports.truncate = void 0; var tslib_1 = require("tslib"); var icons_1 = require("@material-ui/icons"); var plotly_js_basic_dist_1 = (0, tslib_1.__importDefault)(require("plotly.js-basic-dist")); var react_1 = (0, tslib_1.__importStar)(require("react")); var react_bootstrap_1 = require("react-bootstrap"); var factory_1 = (0, tslib_1.__importDefault)(require("react-plotly.js/factory")); var react_sizeme_1 = require("react-sizeme"); var react_tooltip_1 = (0, tslib_1.__importDefault)(require("react-tooltip")); var ColorGradient_1 = (0, tslib_1.__importDefault)(require("../../../containers/ColorGradient")); var ElementWithTooltip_1 = require("../../../containers/widgets/ElementWithTooltip"); var utils_1 = require("../../../utils"); var unCamelCase_1 = require("../../../utils/functions/unCamelCase"); var SynapseContext_1 = require("../../../utils/SynapseContext"); var synapseTypes_1 = require("../../../utils/synapseTypes"); var LoadingScreen_1 = (0, tslib_1.__importDefault)(require("../../LoadingScreen")); var EnumFacetFilter_1 = require("../query-filter/EnumFacetFilter"); var QueryFilter_1 = require("../query-filter/QueryFilter"); var Plot = (0, factory_1.default)(plotly_js_basic_dist_1.default); var maxLabelLength = 19; var maxLegendLength = 30; var layout = { showlegend: false, annotations: [], margin: { l: 0, r: 0, b: 0, t: 0, pad: 0 }, yaxis: { visible: false, showgrid: false, }, xaxis: { visible: false, showgrid: false, }, }; // https://github.com/plotly/plotly.js/blob/fa51e33d3e1f8ca0c029b3029f3d006a5205c8f3/src/lib/index.js#L1173 var formatPercent = function (ratio, n) { n = n || 0; var str = (Math.round(100 * ratio * Math.pow(10, n)) * Math.pow(0.1, n)).toFixed(n) + '%'; for (var i = 0; i < n; i++) { if (str.indexOf('.') !== -1) { str = str.replace('0%', '%'); str = str.replace('.%', '%'); } } return str; }; function truncate(str, n) { if (!str) { return str; } var trimmedStr = str.trim(); return trimmedStr.length > n ? trimmedStr.substr(0, n - 1) + '…' : str; } exports.truncate = truncate; function extractPlotDataArray(facetToPlot, columnType, index, plotType, accessToken, facetAliases) { var _a, _b; return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var colorPalette, getLabels, getLabel, labels, text, anyFacetsSelected, selectionAwareColorPalette, singleChartData, result; var _this = this; return (0, tslib_1.__generator)(this, function (_c) { switch (_c.label) { case 0: colorPalette = (0, ColorGradient_1.default)(index, facetToPlot.facetValues.length).colorPalette; getLabels = function (facetValues, columnType, accessToken) { return (0, tslib_1.__awaiter)(_this, void 0, void 0, function () { var map, filteredValues, response, _i, _a, header, response, _b, _c, header; return (0, tslib_1.__generator)(this, function (_d) { switch (_d.label) { case 0: map = new Map(); map.set(utils_1.SynapseConstants.VALUE_NOT_SET, utils_1.SynapseConstants.FRIENDLY_VALUE_NOT_SET); filteredValues = facetValues .map(function (value) { return value.value; }) .filter(function (val) { return val !== utils_1.SynapseConstants.VALUE_NOT_SET; }); if (!(columnType === synapseTypes_1.ColumnType.ENTITYID)) return [3 /*break*/, 2]; return [4 /*yield*/, utils_1.SynapseClient.getEntityHeadersByIds(filteredValues, accessToken)]; case 1: response = _d.sent(); for (_i = 0, _a = response.results; _i < _a.length; _i++) { header = _a[_i]; map.set(header.id, header.name); } return [3 /*break*/, 4]; case 2: if (!(columnType === synapseTypes_1.ColumnType.USERID)) return [3 /*break*/, 4]; return [4 /*yield*/, utils_1.SynapseClient.getGroupHeadersBatch(filteredValues, accessToken)]; case 3: response = _d.sent(); for (_b = 0, _c = response.children; _b < _c.length; _b++) { header = _c[_b]; map.set(header.ownerId, header.userName); } _d.label = 4; case 4: return [2 /*return*/, facetValues.map(function (facetValue) { return ({ facet: facetValue, label: getLabel(facetValue, false, map), truncatedLabel: getLabel(facetValue, true, map), count: facetValue.count, }); })]; } }); }); }; getLabel = function (facetValue, truncateFlag, labelMap) { var _a; var label = (_a = labelMap.get(facetValue.value)) !== null && _a !== void 0 ? _a : facetValue.value; if (truncateFlag) { label = truncate(label, maxLabelLength); } return label; }; return [4 /*yield*/, getLabels(facetToPlot.facetValues, columnType, accessToken)]; case 1: labels = _c.sent(); text = labels.map(function (el) { return el.truncatedLabel; }); anyFacetsSelected = facetToPlot.facetValues.some(function (value) { return value.isSelected; }); selectionAwareColorPalette = anyFacetsSelected ? facetToPlot.facetValues.map(function (facetValue, index) { return facetValue.isSelected ? colorPalette[index] : colorPalette[index] .replace('rgb(', 'rgba(') .replace(')', ', 0.25)'); }) : colorPalette; singleChartData = { values: plotType === 'PIE' ? facetToPlot.facetValues.map(function (facet) { return facet.count; }) : undefined, labels: labels.map(function (el) { return el.label; }), text: text, x: plotType === 'BAR' ? facetToPlot.facetValues.map(function (facet) { var _a, _b; return (_b = (_a = labels.find(function (label) { return label.facet === facet; })) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : facet.value; }) : undefined, y: plotType === 'BAR' ? facetToPlot.facetValues.map(function (facet) { return facet.count; }) : undefined, // @ts-ignore facetEnumerationValues: facetToPlot.facetValues.map(function (facetValue) { return facetValue.value; }), name: facetToPlot.columnName, hovertemplate: plotType === 'PIE' ? '<b>%{text}</b><br>%{value} (%{percent})<br><extra></extra>' : '<b>%{text}: </b><br>%{value} <br><extra></extra>', textinfo: 'none', type: plotType === 'PIE' ? 'pie' : 'bar', pull: plotType === 'PIE' ? facetToPlot.facetValues.map(function (facetValue) { return facetValue.isSelected ? 0.1 : 0; }) : undefined, marker: { colors: plotType === 'PIE' ? selectionAwareColorPalette : undefined, color: plotType === 'BAR' ? selectionAwareColorPalette : undefined, }, }; result = { data: [singleChartData], labels: labels, colors: plotType === 'PIE' ? (_a = singleChartData.marker) === null || _a === void 0 ? void 0 : _a.colors : (_b = singleChartData.marker) === null || _b === void 0 ? void 0 : _b.color, }; return [2 /*return*/, result]; } }); }); } exports.extractPlotDataArray = extractPlotDataArray; var applyFacetFilter = function (event, allFacetValues, callbackApplyFn) { if (event.points && event.points[0]) { var plotPointData = event.points[0]; var facetValueClickedValue_1 = plotPointData.data.facetEnumerationValues[plotPointData.pointNumber]; var facetValueClicked = allFacetValues.facetValues.find(function (facet) { return facet.value === facetValueClickedValue_1; }); callbackApplyFn(allFacetValues, facetValueClicked, !facetValueClicked.isSelected); } }; function getPlotStyle(parentWidth, plotType, maxHeight) { var quotient = plotType === 'BAR' ? 0.8 : 0.6; var width = parentWidth ? parentWidth * quotient : 200; var height = plotType === 'PIE' ? width : width / 3; // max height of .FacetNav row col* is 200px, so the effective plot height max is around 150 unless it's expanded if (height > maxHeight) { height = maxHeight; } return { width: width + "px", height: height + "px", }; } exports.getPlotStyle = getPlotStyle; function FacetPlotLegend(props) { var labels = props.labels, _a = props.colors, colors = _a === void 0 ? [] : _a, isExpanded = props.isExpanded; if (!labels) { return react_1.default.createElement(react_1.default.Fragment, null); } var numLegendItems = isExpanded ? Math.min(labels.length, 9) : Math.min(labels.length, 3); if (numLegendItems === 0) { return react_1.default.createElement(react_1.default.Fragment, null); } var totalCount = labels.reduce(function (curValue, curFacet) { return curValue + curFacet.count; }, 0); return (react_1.default.createElement("div", { className: "FacetNavPanel__body__legend" + (isExpanded ? '--expanded' : '') }, labels.slice(0, numLegendItems).map(function (facetValue, index) { var percent = formatPercent(facetValue.count / totalCount, 1); var label = "(" + percent + ") " + facetValue.label; var labelDisplay = truncate(label, maxLegendLength); return (react_1.default.createElement(ElementWithTooltip_1.ElementWithTooltip, { idForToolTip: facetValue.label, tooltipText: facetValue.label, key: facetValue.label }, react_1.default.createElement("div", { className: "FacetNavPanel__body__legend__row", key: "legendLabel_" + index, style: { cursor: 'default' } }, react_1.default.createElement("div", { style: { backgroundColor: colors[index] } }), react_1.default.createElement("label", null, labelDisplay)))); }))); } exports.FacetPlotLegend = FacetPlotLegend; var getClassNameForPlotDiv = function (isExpanded, plotType) { if (!isExpanded) { return 'FacetNavPanel__body__plot'; } return "FacetNavPanel__body__plot--expanded" + (plotType === 'BAR' ? 'Bar' : 'Pie'); }; var FacetNavPanel = function (props) { var onHide = props.onHide, isModalView = props.isModalView, applyChangesToFacetFilter = props.applyChangesToFacetFilter, applyChangesToGraphSlice = props.applyChangesToGraphSlice, isLoadingNewData = props.isLoadingNewData, index = props.index, facetToPlot = props.facetToPlot, data = props.data, isLoading = props.isLoading, facetAliases = props.facetAliases, lastQueryRequest = props.lastQueryRequest, plotType = props.plotType, onSetPlotType = props.onSetPlotType; var accessToken = (0, SynapseContext_1.useSynapseContext)().accessToken; var _a = (0, react_1.useState)(), plotData = _a[0], setPlotData = _a[1]; var _b = (0, react_1.useState)(false), showModal = _b[0], setShowModal = _b[1]; var plotTitle = (0, unCamelCase_1.unCamelCase)(facetToPlot.columnName, facetAliases); var TOOLTIP_ID = 'facet-nav-panel-tooltip'; var getColumnType = (0, react_1.useCallback)(function () { var _a, _b; return (_b = (_a = data === null || data === void 0 ? void 0 : data.columnModels) === null || _a === void 0 ? void 0 : _a.find(function (columnModel) { return columnModel.name === facetToPlot.columnName; })) === null || _b === void 0 ? void 0 : _b.columnType; }, [data, facetToPlot.columnName]); (0, react_1.useEffect)(function () { if (!facetToPlot) { return; } else { extractPlotDataArray(facetToPlot, getColumnType(), index, plotType, accessToken).then(function (plotData) { return setPlotData(plotData); }); } }, [facetToPlot, data, index, plotType, accessToken, getColumnType]); /* rendering functions */ var ChartSelectionToggle = function () { return (react_1.default.createElement("div", { onClick: function (event) { event.stopPropagation(); }, className: "bootstrap-4-backport SRC-labeled-dropdown" }, react_1.default.createElement("span", { className: "SRC-labeled-dropdown__label" }, "Chart Type"), react_1.default.createElement(react_bootstrap_1.Dropdown, null, react_1.default.createElement(react_bootstrap_1.Dropdown.Toggle, { className: "secondary-caret", variant: "gray-select" }, plotType === 'PIE' ? 'Pie Chart' : 'Bar Chart'), react_1.default.createElement(react_bootstrap_1.Dropdown.Menu, { className: "chart-tools" }, react_1.default.createElement(react_bootstrap_1.Dropdown.Item, { as: "button", onClick: function () { return onSetPlotType('BAR'); } }, "Bar Chart"), react_1.default.createElement(react_bootstrap_1.Dropdown.Item, { as: "button", onClick: function () { return onSetPlotType('PIE'); } }, "Pie Chart"))))); }; if (isLoadingNewData || !facetToPlot) { return (react_1.default.createElement("div", { className: "SRC-loadingContainer SRC-centerContentColumn" }, LoadingScreen_1.default)); } else { return (react_1.default.createElement(react_1.default.Fragment, null, react_1.default.createElement(react_bootstrap_1.Modal, { animation: false, show: showModal, onHide: function () { return setShowModal(false); } }, react_1.default.createElement(react_bootstrap_1.Modal.Header, { closeButton: true }, react_1.default.createElement(react_bootstrap_1.Modal.Title, null, plotTitle !== null && plotTitle !== void 0 ? plotTitle : '')), react_1.default.createElement(react_bootstrap_1.Modal.Body, null, react_1.default.createElement(FacetNavPanel, (0, tslib_1.__assign)({}, props, { isModalView: true })), react_1.default.createElement("div", { className: "bootstrap-4-backport SaveFiltersButtonContainer" }, react_1.default.createElement(react_bootstrap_1.Button, { variant: "secondary", className: "pill-xl SaveFiltersButton", size: "sm", onClick: function () { return setShowModal(false); } }, "Apply Filters")))), react_1.default.createElement("div", { role: "graphics-document", className: "FacetNavPanel" + (isModalView ? '--expanded' : '') }, !isModalView && (react_1.default.createElement("div", { className: "FacetNavPanel__title" }, react_1.default.createElement("span", { className: "FacetNavPanel__title__name" }, plotTitle), isLoading && (react_1.default.createElement("span", { style: { marginLeft: '2px' }, className: 'spinner' })), react_1.default.createElement("div", { className: "FacetNavPanel__title__tools" }, react_1.default.createElement(EnumFacetFilter_1.EnumFacetFilter, { facetValues: facetToPlot.facetValues, columnModel: data === null || data === void 0 ? void 0 : data.columnModels.find(function (el) { return el.name === facetToPlot.columnName; }), facetAliases: facetAliases, onChange: function (facetNamesMap) { (0, QueryFilter_1.applyMultipleChangesToValuesColumn)(lastQueryRequest, facetToPlot, applyChangesToFacetFilter, facetNamesMap); }, onClear: function () { (0, QueryFilter_1.applyChangesToValuesColumn)(lastQueryRequest, facetToPlot, applyChangesToFacetFilter); }, containerAs: "Dropdown" }), react_1.default.createElement(ElementWithTooltip_1.ElementWithTooltip, { idForToolTip: "expandGraph", tooltipText: "Expand to large graph", key: "expandGraph", callbackFn: function () { return setShowModal(true); }, className: "SRC-primary-color", darkTheme: false, icon: 'expand' }), react_1.default.createElement(ElementWithTooltip_1.ElementWithTooltip, { idForToolTip: "hideGraph", tooltipText: "Hide graph under Show More", key: "hideGraph", callbackFn: function () { return onHide(); }, className: "SRC-primary-color", darkTheme: false, icon: 'close' })))), isModalView && (react_1.default.createElement(react_1.default.Fragment, null, react_1.default.createElement("div", { className: 'bootstrap-4-backport SRC-labeled-dropdown' }, react_1.default.createElement("span", { className: "SRC-labeled-dropdown__label" }, "Filter All Data By"), react_1.default.createElement(EnumFacetFilter_1.EnumFacetFilter, { facetValues: facetToPlot.facetValues, columnModel: data === null || data === void 0 ? void 0 : data.columnModels.find(function (el) { return el.name === facetToPlot.columnName; }), facetAliases: facetAliases, onChange: function (facetNamesMap) { (0, QueryFilter_1.applyMultipleChangesToValuesColumn)(lastQueryRequest, facetToPlot, applyChangesToFacetFilter, facetNamesMap); }, onClear: function () { (0, QueryFilter_1.applyChangesToValuesColumn)(lastQueryRequest, facetToPlot, applyChangesToFacetFilter); }, containerAs: "Dropdown", dropdownType: "SelectBox" }), react_1.default.createElement(react_tooltip_1.default, { id: TOOLTIP_ID, effect: "solid", event: "click" }), react_1.default.createElement(icons_1.InfoOutlined, { "data-for": TOOLTIP_ID, "data-tip": 'Selecting items in this dropdown will affect all facets on the Explore page.', className: "SRC-hand-cursor SRC-secondary-text-color" })), react_1.default.createElement(ChartSelectionToggle, null))), react_1.default.createElement("div", { className: "FacetNavPanel__body" + (isModalView ? '--expanded' : '') }, react_1.default.createElement(react_sizeme_1.SizeMe, { monitorHeight: true }, function (_a) { var _b; var size = _a.size; return (react_1.default.createElement("div", { className: getClassNameForPlotDiv(isModalView, plotType) }, react_1.default.createElement(Plot, { key: facetToPlot.columnName + "-" + plotType + "-" + size.width, layout: layout, data: (_b = plotData === null || plotData === void 0 ? void 0 : plotData.data) !== null && _b !== void 0 ? _b : [], style: getPlotStyle(size.width, plotType, isModalView ? 300 : 150), config: { displayModeBar: false }, onClick: function (evt) { return applyFacetFilter(evt, facetToPlot, applyChangesToGraphSlice); } }))); }), react_1.default.createElement(FacetPlotLegend, { labels: plotData === null || plotData === void 0 ? void 0 : plotData.labels, colors: plotData === null || plotData === void 0 ? void 0 : plotData.colors, isExpanded: isModalView }))))); } }; exports.default = FacetNavPanel; //# sourceMappingURL=FacetNavPanel.js.map