ucsc-xena-client
Version:
UCSC Xena Client. Functional genomics visualizations.
452 lines (393 loc) • 16.7 kB
JavaScript
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _PureComponent2 = require('./PureComponent');
var _PureComponent3 = _interopRequireDefault(_PureComponent2);
var _app_bar = require('react-toolbox/lib/app_bar');
var _app_bar2 = _interopRequireDefault(_app_bar);
var _fieldSpec = require('./models/fieldSpec');
var _datasetJoins = require('./models/datasetJoins');
var _SampleSearch = require('./views/SampleSearch');
var _uuid = require('./uuid');
var _uuid2 = _interopRequireDefault(_uuid);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
var React = require('react');
var pdf = require('./pdfSpreadsheet');
var _ = require('./underscore_ext');
var konami = require('./konami');
var widgets = require('./columnWidgets');
var classNames = require('classnames');
var gaEvents = require('./gaEvents');
// Styles
var compStyles = require('./AppControls.module.css');
var modeIcon = {
chart: 'view_column',
heatmap: 'insert_chart'
};
var modeEvent = {
chart: 'heatmap',
heatmap: 'chart'
};
var modeHelp = {
chart: 'View as columns',
heatmap: 'View as chart'
};
function download(_ref) {
var _ref2 = _slicedToArray(_ref, 2),
fields = _ref2[0],
rows = _ref2[1];
var txt = _.map([fields].concat(rows), function (row) {
return row.join('\t');
}).join('\n');
// use blob for bug in chrome: https://code.google.com/p/chromium/issues/detail?id=373182
var url = URL.createObjectURL(new Blob([txt], { type: 'text/tsv' }));
var a = document.createElement('a');
var filename = 'denseDataOnlyDownload.tsv';
_.extend(a, { id: filename, download: filename, href: url });
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
var asciiB = 66;
var Actions = function Actions(_ref3) {
var onPdf = _ref3.onPdf,
onDownload = _ref3.onDownload,
onShowWelcome = _ref3.onShowWelcome,
showWelcome = _ref3.showWelcome,
onMode = _ref3.onMode,
mode = _ref3.mode,
hasColumn = _ref3.hasColumn;
return React.createElement(
'div',
{ className: compStyles.actions },
hasColumn ? React.createElement(
'i',
{ className: 'material-icons', onClick: onMode, title: modeHelp[mode] },
modeIcon[mode]
) : null,
hasColumn && mode === 'heatmap' ? React.createElement(
'i',
{ className: 'material-icons', onClick: onPdf, title: 'Download as PDF' },
'picture_as_pdf'
) : null,
hasColumn ? React.createElement(
'i',
{ className: 'material-icons', onClick: onDownload, title: 'Download as tsv' },
'cloud_download'
) : null,
showWelcome ? null : React.createElement(
'i',
{ className: 'material-icons', onClick: onShowWelcome },
'help'
)
);
};
var BasicSearch = function BasicSearch(_ref4) {
var help = _ref4.help,
onTies = _ref4.onTies,
tiesEnabled = _ref4.tiesEnabled,
searchProps = _objectWithoutProperties(_ref4, ['help', 'onTies', 'tiesEnabled']);
return React.createElement(
'div',
{ className: compStyles.filter },
React.createElement(_SampleSearch.SampleSearch, searchProps),
help ? React.createElement(
'a',
{ href: help, target: '_blank', className: compStyles.filterHelp },
React.createElement(
'i',
{ className: 'material-icons' },
'help_outline'
)
) : null,
tiesEnabled ? React.createElement(
'a',
{ onClick: onTies, className: compStyles.ties },
React.createElement(
'i',
{ className: 'material-icons' },
'toys'
)
) : null
);
};
var TiesSearch = function TiesSearch() {
return React.createElement(
'div',
{ className: compStyles.filter },
React.createElement(
'span',
null,
'Pathology Report Search and Filter by TIES'
)
);
};
var TiesActions = function TiesActions(_ref5) {
var onTies = _ref5.onTies,
onTiesColumn = _ref5.onTiesColumn;
return React.createElement(
'div',
{ className: compStyles.actions },
React.createElement(
'button',
{ onClick: onTiesColumn },
'Create filtered column'
),
React.createElement(
'a',
{ onClick: onTies, className: compStyles.filterHelp },
React.createElement(
'i',
{ className: 'material-icons' },
'close'
)
)
);
};
function getFilterColumn(title, samples) {
var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var field = (0, _fieldSpec.signatureField)(title, _extends({
columnLabel: 'filter',
valueType: 'coded',
signature: ['in', samples]
}, opts)),
colSpec = (0, _datasetJoins.getColSpec)([field], []),
settings = _.assoc(colSpec, 'width', 136, 'user', _.pick(colSpec, ['columnLabel', 'fieldLabel']));
return { id: (0, _uuid2.default)(), settings: settings };
}
// XXX drop this.props.style? Not sure it's used.
var AppControls = function (_PureComponent) {
_inherits(AppControls, _PureComponent);
function AppControls() {
var _ref6;
var _temp, _this, _ret;
_classCallCheck(this, AppControls);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref6 = AppControls.__proto__ || Object.getPrototypeOf(AppControls)).call.apply(_ref6, [this].concat(args))), _this), _this.onFilter = function () {
var _this$props = _this.props,
callback = _this$props.callback,
_this$props$appState = _this$props.appState,
samplesMatched = _this$props$appState.samplesMatched,
cohortSamples = _this$props$appState.cohortSamples,
matching = _.map(samplesMatched, function (i) {
return cohortSamples[i];
});
gaEvents('spreadsheet', 'samplesearch', 'filter');
callback(['sampleFilter', matching]);
}, _this.onFilterZoom = function () {
var _this$props2 = _this.props,
_this$props2$appState = _this$props2.appState,
samples = _this$props2$appState.samples,
samplesMatched = _this$props2$appState.samplesMatched,
height = _this$props2$appState.zoom.height,
callback = _this$props2.callback,
toOrder = _.object(samples, _.range(samples.length)),
index = toOrder[_.min(samplesMatched, function (s) {
return toOrder[s];
})],
last = toOrder[_.max(samplesMatched, function (s) {
return toOrder[s];
})];
gaEvents('spreadsheet', 'samplesearch', 'zoom');
callback(['zoom', { index: index, height: height, count: last - index + 1 }]);
}, _this.onFilterColumn = function () {
var _this$props3 = _this.props,
_this$props3$appState = _this$props3.appState,
cohortSamples = _this$props3$appState.cohortSamples,
sampleSearch = _this$props3$appState.sampleSearch,
samplesMatched = _this$props3$appState.samplesMatched,
callback = _this$props3.callback,
matching = _.map(samplesMatched, function (i) {
return cohortSamples[i];
});
gaEvents('spreadsheet', 'samplesearch', 'new column');
callback(['add-column', 0, getFilterColumn(sampleSearch, matching, { filter: sampleSearch })]);
}, _this.onTiesColumn = function () {
var _this$props4 = _this.props,
_this$props4$appState = _this$props4.appState,
_this$props4$appState2 = _this$props4$appState.ties,
filter = _this$props4$appState2.filter,
docs = _this$props4$appState2.docs,
cohortSamples = _this$props4$appState.cohortSamples,
patient = _this$props4$appState.survival.patient,
callback = _this$props4.callback,
pindex = patient.data.req.values[0],
pcodes = patient.data.codes,
keep = new Set(Object.keys(filter).filter(function (k) {
return filter[k];
}).map(function (i) {
return docs[i].patient;
})),
matching = cohortSamples.filter(function (s, i) {
return keep.has(pcodes[pindex[i]]);
});
callback(['add-column', 0, getFilterColumn('TIES selection', matching)]);
callback(['ties-dismiss']);
}, _this.onMode = function () {
var _this$props5 = _this.props,
callback = _this$props5.callback,
mode = _this$props5.appState.mode;
gaEvents('spreadsheet', 'mode', modeEvent[mode]);
callback([modeEvent[mode]]);
}, _this.onRefresh = function () {
var callback = _this.props.callback;
callback(['refresh-cohorts']);
}, _this.onPdf = function () {
gaEvents('spreadsheet', 'pdf', 'spreadsheet');
pdf(_this.props.appState);
}, _this.onCohortSelect = function (value) {
_this.props.callback(['cohort', value]);
}, _this.onTies = function () {
var ties = _this.props.appState.ties;
_this.props.callback([_.get(ties, 'open') ? 'ties-dismiss' : 'ties-open']);
}, _this.onDownload = function () {
var sampleFormat = _this.props.sampleFormat,
_this$props$appState2 = _this.props.appState,
samples = _this$props$appState2.samples,
columns = _this$props$appState2.columns,
columnOrder = _this$props$appState2.columnOrder,
index = _this$props$appState2.index,
data = _this$props$appState2.data,
rectData = columnOrder.filter(function (id) {
return _.contains(['float', 'coded', 'segmented'], columns[id].valueType) && _.getIn(data, [id, 'status']) === 'loaded';
}),
datasets = rectData.map(function (id) {
return widgets.download({ samples: samples, column: columns[id], index: index[id], data: data[id], sampleFormat: sampleFormat });
}),
combinedRows = _.mmap.apply(_, _toConsumableArray(_.pluck(datasets, 1)).concat([function () {
for (var _len2 = arguments.length, rows = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
rows[_key2] = arguments[_key2];
}
rows.pop(); // mmap passes index
return [].concat(_toConsumableArray(rows[0]), _toConsumableArray(_.flatten(rows.slice(1).map(function (cols) {
return cols.slice(1);
}))));
}])),
combinedHeaders = _.flatmap(datasets, function (_ref7, i) {
var _ref8 = _slicedToArray(_ref7, 1),
headers = _ref8[0];
return i === 0 ? headers : headers.slice(1);
});
gaEvents('spreadsheet', 'download', 'spreadsheet');
download([combinedHeaders, combinedRows]);
}, _this.onShowWelcome = function () {
_this.props.onShowWelcome();
}, _temp), _possibleConstructorReturn(_this, _ret);
}
_createClass(AppControls, [{
key: 'componentWillMount',
value: function componentWillMount() {
var _this2 = this;
this.nsub = konami(asciiB).subscribe(function () {
_this2.props.callback(['notifications-enable']);
});
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
this.nsub.unsubscribe();
}
}, {
key: 'render',
value: function render() {
var _props = this.props,
_props$appState = _props.appState,
cohort = _props$appState.cohort,
mode = _props$appState.mode,
columnOrder = _props$appState.columnOrder,
showWelcome = _props$appState.showWelcome,
samples = _props$appState.samples,
sampleSearch = _props$appState.sampleSearch,
samplesMatched = _props$appState.samplesMatched,
ties = _props$appState.ties,
onReset = _props.onReset,
help = _props.help,
onResetSampleFilter = _props.onResetSampleFilter,
onHighlightChange = _props.onHighlightChange,
callback = _props.callback,
matches = _.get(samplesMatched, 'length', samples.length),
onPdf = this.onPdf,
onDownload = this.onDownload,
onShowWelcome = this.onShowWelcome,
onMode = this.onMode,
tiesOpen = _.get(ties, 'open'),
cohortName = _.get(cohort, 'name'),
hasColumn = !!columnOrder.length,
index = _.getIn(this.props, ['zoom', 'index']) || 0,
count = _.getIn(this.props, ['zoom', 'count']) || 0,
sampleFilter = _.get(cohort, 'sampleFilter'),
filter = sampleFilter ? React.createElement(
'span',
{ onClick: onResetSampleFilter, className: compStyles.appliedFilter },
'Filtered to '
) : null,
fraction = count === samples.length ? '' : '- Zoomed to ' + (index + 1) + ' - ' + (index + count);
return React.createElement(
_app_bar2.default,
null,
React.createElement(
'div',
{ className: classNames(compStyles.appBarContainer, compStyles.cohort) },
React.createElement(
'div',
{ className: compStyles.titleContainer },
React.createElement(
'span',
{ className: compStyles.title },
cohortName
),
React.createElement(
'span',
{ className: compStyles.subtitle },
filter,
' ',
samples.length,
' Samples ',
fraction ? fraction : null
)
),
React.createElement(
'i',
{ className: 'material-icons', onClick: this.onRefresh, title: 'Reload cohort data' },
'refresh'
),
React.createElement(
'i',
{ className: 'material-icons', onClick: onReset, title: 'Pick new cohort' },
'close'
)
),
React.createElement(
'div',
{ className: classNames(compStyles.appBarContainer, compStyles.tools) },
tiesOpen ? React.createElement(TiesSearch, { onTies: this.onTies }) : React.createElement(BasicSearch, {
value: sampleSearch,
matches: matches,
sampleCount: samples.length,
onFilter: this.onFilter,
onZoom: this.onFilterZoom,
onCreateColumn: this.onFilterColumn,
onChange: onHighlightChange,
mode: mode,
onResetSampleFilter: onResetSampleFilter,
cohort: cohort,
callback: callback,
help: help,
onTies: this.onTies,
tiesEnabled: false }),
tiesOpen ? React.createElement(TiesActions, { onTies: this.onTies, onTiesColumn: this.onTiesColumn }) : React.createElement(Actions, { onPdf: onPdf, onDownload: onDownload, onShowWelcome: onShowWelcome, showWelcome: showWelcome, onMode: onMode, mode: mode, hasColumn: hasColumn })
)
);
}
}]);
return AppControls;
}(_PureComponent3.default);
module.exports = { AppControls: AppControls };