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