@vitessce/biomarker-select 3.9.2 → 3.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ export function BiomarkerSelectAlt(props: any): JSX.Element;
2
+ //# sourceMappingURL=BiomarkerSelectAlt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BiomarkerSelectAlt.d.ts","sourceRoot":"","sources":["../src/BiomarkerSelectAlt.js"],"names":[],"mappings":"AAOA,4DAyCC"}
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { Grid } from '@vitessce/styles';
4
+ import { BiomarkerSelectAltGeneAutocomplete } from './BiomarkerSelectAltGeneAutocomplete.js';
5
+ import { BiomarkerSelectAltSampleGroups } from './BiomarkerSelectAltSampleGroups.js';
6
+ import { useStyles } from './styles.js';
7
+ export function BiomarkerSelectAlt(props) {
8
+ const { setFeatureSelection, setSampleSetFilter, setSampleSetSelection, currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection, setCurrentModalitySpecificSelection, autocompleteNode, stratifications, } = props;
9
+ const { classes } = useStyles();
10
+ return (_jsx(Grid, { container: true, sx: { border: '0px solid red' }, children: _jsxs(Grid, { container: true, size: 12, className: classes.header, children: [_jsx(Grid, { container: true, size: 4, justifyContent: "flex-end", children: _jsx(BiomarkerSelectAltGeneAutocomplete, { setFeatureSelection: setFeatureSelection, autocompleteNode: autocompleteNode, currentModalityAgnosticSelection: currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection: setCurrentModalityAgnosticSelection, setCurrentModalitySpecificSelection: setCurrentModalitySpecificSelection }) }), _jsx(Grid, { container: true, size: 5, justifyContent: "flex-start", flexDirection: "column", children: _jsx(BiomarkerSelectAltSampleGroups, { setSampleSetFilter: setSampleSetFilter, setSampleSetSelection: setSampleSetSelection, stratifications: stratifications }) })] }) }));
11
+ }
@@ -0,0 +1,2 @@
1
+ export function BiomarkerSelectAltGeneAutocomplete(props: any): JSX.Element;
2
+ //# sourceMappingURL=BiomarkerSelectAltGeneAutocomplete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BiomarkerSelectAltGeneAutocomplete.d.ts","sourceRoot":"","sources":["../src/BiomarkerSelectAltGeneAutocomplete.js"],"names":[],"mappings":"AAYA,4EA0FC"}
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /* eslint-disable no-unused-vars */
3
+ import React, { useState } from 'react';
4
+ import { Grid, Button, Typography, Dialog, Info as InfoIcon, SimpleAutocomplete, } from '@vitessce/styles';
5
+ import { useStyles } from './styles.js';
6
+ export function BiomarkerSelectAltGeneAutocomplete(props) {
7
+ const { setFeatureSelection, autocompleteNode, currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection, setCurrentModalitySpecificSelection, } = props;
8
+ const { classes } = useStyles();
9
+ const [selectedItem, setSelectedItem] = useState(null);
10
+ const [biomarkerInfoOpen, setBiomarkerInfoOpen] = useState(false);
11
+ function confirmSelectedItem() {
12
+ // eslint-disable-next-line max-len
13
+ if (selectedItem && !currentModalityAgnosticSelection?.find(item => item.kgId === selectedItem?.kgId)) {
14
+ setCurrentModalityAgnosticSelection([
15
+ ...(currentModalityAgnosticSelection || []),
16
+ selectedItem,
17
+ ]);
18
+ // TODO: directly set featureSelection coordination value here instead?
19
+ // console.log('Setting specific selection to:', selectedItem);
20
+ const nextModalitySpecificSelection = [
21
+ ...(currentModalityAgnosticSelection || []),
22
+ selectedItem,
23
+ ];
24
+ setFeatureSelection(nextModalitySpecificSelection.map(d => d.label));
25
+ }
26
+ }
27
+ function clearBiomarkerSelection() {
28
+ setCurrentModalityAgnosticSelection([]);
29
+ setCurrentModalitySpecificSelection([]);
30
+ setFeatureSelection([]);
31
+ }
32
+ return (_jsxs(_Fragment, { children: [_jsxs(Grid, { container: true, size: 12, children: [_jsx(Grid, { size: 8, children: _jsx(SimpleAutocomplete, { getMatches: autocompleteNode, onChange: item => setSelectedItem(item.data), textInputLabel: "Enter a gene", getItemLabel: item => `${item.label} (${item.data.nodeType})` }) }), selectedItem ? (_jsxs(Grid, { size: 4, children: ["\u00A0", _jsx(Button, { variant: "contained", onClick: confirmSelectedItem, children: "Select" })] })) : null] }), selectedItem ? (_jsxs(_Fragment, { children: [_jsx(Grid, { container: true, size: 12, flexDirection: "row", children: _jsxs(Button, { onClick: () => setBiomarkerInfoOpen(true), children: ["View ", selectedItem.nodeType, " info"] }) }), _jsxs(Dialog, { open: biomarkerInfoOpen, onClose: () => setBiomarkerInfoOpen(false), maxWidth: "md", children: [_jsx(Typography, { variant: "h4", title: selectedItem.term, children: selectedItem.label }), _jsxs(Grid, { container: true, size: 12, children: [_jsx(InfoIcon, {}), _jsxs(Typography, { variant: "h6", children: ["About this ", selectedItem.nodeType] })] }), _jsx(Grid, { container: true, size: 12, sx: { width: '800px' }, children: _jsx("iframe", { title: `Embedded metadata page for ontology term ${selectedItem.term}`, src: `https://identifiers.org/${selectedItem.term}`, width: "100%", height: "500", style: { border: 0 } }) }, selectedItem.term)] })] })) : null] }));
33
+ }
@@ -0,0 +1,2 @@
1
+ export function BiomarkerSelectAltSampleGroups(props: any): JSX.Element;
2
+ //# sourceMappingURL=BiomarkerSelectAltSampleGroups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BiomarkerSelectAltSampleGroups.d.ts","sourceRoot":"","sources":["../src/BiomarkerSelectAltSampleGroups.js"],"names":[],"mappings":"AAKA,wEAmJC"}
@@ -0,0 +1,92 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useState, useMemo } from 'react';
3
+ import { Grid, NativeSelect } from '@vitessce/styles';
4
+ import { isEqual } from 'lodash-es';
5
+ export function BiomarkerSelectAltSampleGroups(props) {
6
+ const { setSampleSetFilter, setSampleSetSelection, stratifications, } = props;
7
+ const [lhsSelectedGroup, setLhsSelectedGroup] = useState(null);
8
+ const [rhsSelectedGroup, setRhsSelectedGroup] = useState(null);
9
+ const sampleSetOptions = stratifications?.filter(s => s.stratificationType === 'sampleSet');
10
+ const hasSampleSetOptions = sampleSetOptions && sampleSetOptions.length > 0;
11
+ const lhsOptions = useMemo(() => {
12
+ if (sampleSetOptions) {
13
+ const result = [];
14
+ const withDuplicates = sampleSetOptions.map(o => o.sampleSets).flat();
15
+ withDuplicates.forEach((setPath) => {
16
+ if (!result.find(r => isEqual(r.path, setPath))) {
17
+ const rhsOptionsForLhs = [];
18
+ sampleSetOptions.forEach((o) => {
19
+ const pathPair = o.sampleSets;
20
+ if (isEqual(pathPair[0], setPath)) {
21
+ const otherPath = pathPair[1];
22
+ rhsOptionsForLhs.push({
23
+ name: `${otherPath[0]}: ${otherPath[1]}`,
24
+ path: otherPath,
25
+ sampleSets: o.sampleSets,
26
+ stratificationId: o.stratificationId,
27
+ });
28
+ }
29
+ else if (isEqual(pathPair[1], setPath)) {
30
+ const otherPath = pathPair[0];
31
+ rhsOptionsForLhs.push({
32
+ name: `${otherPath[0]}: ${otherPath[1]}`,
33
+ path: otherPath,
34
+ // Reverse the order.
35
+ sampleSets: [o.sampleSets[1], o.sampleSets[0]],
36
+ stratificationId: o.stratificationId,
37
+ });
38
+ }
39
+ });
40
+ result.push({
41
+ name: `${setPath[0]}: ${setPath[1]}`,
42
+ path: setPath,
43
+ rhsOptions: rhsOptionsForLhs,
44
+ });
45
+ }
46
+ });
47
+ return result;
48
+ }
49
+ return [];
50
+ }, [sampleSetOptions]);
51
+ const rhsOptions = useMemo(() => {
52
+ if (sampleSetOptions && lhsOptions && lhsSelectedGroup) {
53
+ const lhsOption = lhsOptions.find(o => o.name === lhsSelectedGroup);
54
+ return lhsOption ? lhsOption.rhsOptions : [];
55
+ }
56
+ return [];
57
+ }, [sampleSetOptions, lhsOptions, lhsSelectedGroup]);
58
+ function handleFirstGroupChange(event) {
59
+ setLhsSelectedGroup(event.target.value);
60
+ // Update the sampleSetFilter and sampleSetSelection.
61
+ if (event.target.value === '__all__') {
62
+ setSampleSetFilter(null);
63
+ setSampleSetSelection(null);
64
+ }
65
+ else {
66
+ const lhsOption = lhsOptions.find(o => o.name === event.target.value);
67
+ if (lhsOption) {
68
+ setSampleSetFilter([lhsOption.path]);
69
+ setSampleSetSelection([lhsOption.path]);
70
+ }
71
+ }
72
+ }
73
+ function handleSecondGroupChange(event) {
74
+ setRhsSelectedGroup(event.target.value);
75
+ // Update the sampleSetFilter and sampleSetSelection.
76
+ if (event.target.value === '__none__') {
77
+ const lhsOption = lhsOptions.find(o => o.name === lhsSelectedGroup);
78
+ if (lhsOption) {
79
+ setSampleSetFilter([lhsOption.path]);
80
+ setSampleSetSelection([lhsOption.path]);
81
+ }
82
+ }
83
+ else {
84
+ const rhsOption = rhsOptions.find(o => o.name === event.target.value);
85
+ if (rhsOption) {
86
+ setSampleSetFilter(rhsOption.sampleSets);
87
+ setSampleSetSelection(rhsOption.sampleSets);
88
+ }
89
+ }
90
+ }
91
+ return (_jsxs(_Fragment, { children: [_jsxs(Grid, { container: true, size: 12, children: [_jsx(Grid, { container: true, size: 4, children: _jsx("span", { style: { lineHeight: '50px' }, children: "in:\u00A0" }) }), _jsx(Grid, { container: true, size: 8, children: _jsxs(NativeSelect, { onChange: handleFirstGroupChange, value: lhsSelectedGroup ?? '__all__', variant: "standard", sx: { width: '100%' }, children: [_jsx("option", { value: "__all__", children: "All" }), hasSampleSetOptions ? (_jsx(_Fragment, { children: lhsOptions.map(o => (_jsx("option", { value: o.name, children: o.name }))) })) : null] }) })] }), _jsxs(Grid, { container: true, size: 12, children: [_jsx(Grid, { container: true, size: 4, children: _jsx("span", { style: { lineHeight: '50px' }, children: "compare to:\u00A0" }) }), _jsx(Grid, { container: true, size: 8, children: _jsxs(NativeSelect, { onChange: handleSecondGroupChange, value: rhsSelectedGroup ?? '__none__', variant: "standard", sx: { width: '100%' }, children: [_jsx("option", { value: "__none__", children: "None" }), hasSampleSetOptions ? (_jsx(_Fragment, { children: rhsOptions.map(o => (_jsx("option", { value: o.name, children: o.name }))) })) : null] }) })] })] }));
92
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Alternative biomarker select UI.
3
+ * @param {object} props
4
+ * @param {{ name: string, stratificationType: string, sampleSets: [string[], string[]]}[]} props.stratificationOptions
5
+ * @returns
6
+ */
7
+ export function BiomarkerSelectAltSubscriber(props: {
8
+ stratificationOptions: {
9
+ name: string;
10
+ stratificationType: string;
11
+ sampleSets: [string[], string[]];
12
+ }[];
13
+ }): JSX.Element;
14
+ //# sourceMappingURL=BiomarkerSelectAltSubscriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BiomarkerSelectAltSubscriber.d.ts","sourceRoot":"","sources":["../src/BiomarkerSelectAltSubscriber.js"],"names":[],"mappings":"AAkBA;;;;;GAKG;AACH,oDAHG;IAA+F,qBAAqB,EAA5G;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;KAAC,EAAE;CACvF,eA2JF"}
@@ -0,0 +1,112 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /* eslint-disable max-len */
3
+ /* eslint-disable no-unused-vars */
4
+ import React, { useState, useCallback, useMemo } from 'react';
5
+ import { useAsyncFunction, useViewConfigStoreApi, useSetViewConfig, useViewConfig, useCoordination, useLoaders, useComparisonMetadata, useMatchingLoader, useColumnNameMapping, useCoordinationScopes, } from '@vitessce/vit-s';
6
+ import { AsyncFunctionType, ViewType, COMPONENT_COORDINATION_TYPES, DataType } from '@vitessce/constants-internal';
7
+ import { BiomarkerSelectAlt } from './BiomarkerSelectAlt.js';
8
+ /**
9
+ * Alternative biomarker select UI.
10
+ * @param {object} props
11
+ * @param {{ name: string, stratificationType: string, sampleSets: [string[], string[]]}[]} props.stratificationOptions
12
+ * @returns
13
+ */
14
+ export function BiomarkerSelectAltSubscriber(props) {
15
+ const { coordinationScopes: coordinationScopesRaw, } = props;
16
+ const loaders = useLoaders();
17
+ const coordinationScopes = useCoordinationScopes(coordinationScopesRaw);
18
+ const [{ dataset, obsType, sampleType, sampleSetFilter, sampleSetSelection, featureSelection, }, { setSampleSetFilter, setSampleSetSelection, setFeatureSelection, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.BIOMARKER_SELECT], coordinationScopes);
19
+ const viewConfigStoreApi = useViewConfigStoreApi();
20
+ const viewConfig = useViewConfig();
21
+ const setViewConfig = useSetViewConfig(viewConfigStoreApi);
22
+ // Need obsSets and sampleSets options to obtain mapping between
23
+ // group names and column names.
24
+ const obsSetsLoader = useMatchingLoader(loaders, dataset, DataType.OBS_SETS, { obsType });
25
+ const sampleSetsLoader = useMatchingLoader(loaders, dataset, DataType.SAMPLE_SETS, { sampleType });
26
+ const sampleSetsColumnNameMappingReversed = useColumnNameMapping(sampleSetsLoader, true);
27
+ const [mode] = useState(null);
28
+ const [currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection] = useState(null);
29
+ const [currentModalitySpecificSelection, setCurrentModalitySpecificSelection] = useState(null);
30
+ const [currentStratificationSelection, setCurrentStratificationSelection] = useState(null);
31
+ const autocompleteFeature = useAsyncFunction(AsyncFunctionType.AUTOCOMPLETE_FEATURE);
32
+ const transformFeature = useAsyncFunction(AsyncFunctionType.TRANSFORM_FEATURE);
33
+ const autocompleteNode = useCallback(async (inputValue) => {
34
+ const results = await autocompleteFeature(inputValue);
35
+ return results.map(item => ({
36
+ label: item.label,
37
+ data: item,
38
+ }));
39
+ }, [autocompleteFeature]);
40
+ const getEdges = useCallback(async (node, targetModality) => transformFeature(node, targetModality), [transformFeature]);
41
+ const [{ comparisonMetadata }, cmpMetadataStatus, cmpMetadataUrls] = useComparisonMetadata(loaders, dataset, false, {}, {}, { obsType, sampleType });
42
+ const stratificationOptions = useMemo(() => {
43
+ /*
44
+ return array of objects like {
45
+ stratificationId: 'aki-vs-hr',
46
+ name: 'Acute kidney injury (AKI) vs. Healthy reference',
47
+ stratificationType: 'sampleSet', // key changed from 'groupType'. value changed from 'clinical'
48
+ sampleSets: [
49
+ ['Disease Type', 'AKI'],
50
+ ['Disease Type', 'Reference'],
51
+ ],
52
+ },
53
+ */
54
+ if (comparisonMetadata?.sample_group_pairs) {
55
+ return comparisonMetadata.sample_group_pairs.map((sampleGroupPair) => {
56
+ const [sampleGroupCol, sampleGroupValues] = sampleGroupPair;
57
+ const [sampleGroupCtrl, sampleGroupCase] = sampleGroupValues;
58
+ const groupName = sampleSetsColumnNameMappingReversed?.[sampleGroupCol];
59
+ return {
60
+ stratificationId: `${sampleGroupCol}_${sampleGroupCtrl}-vs-${sampleGroupCase}`,
61
+ name: `${groupName}: ${sampleGroupCtrl} vs. ${sampleGroupCase}`,
62
+ stratificationType: 'sampleSet',
63
+ sampleSets: [
64
+ // With sampleSets coming from the comparison_metadata,
65
+ // need to use loader options from obsSets and sampleSets to get mapping
66
+ // from column name to group name.
67
+ [groupName, sampleGroupCtrl],
68
+ [groupName, sampleGroupCase],
69
+ ],
70
+ };
71
+ });
72
+ }
73
+ return null;
74
+ }, [comparisonMetadata, sampleSetsColumnNameMappingReversed]);
75
+ // TODO: Remove mode/step logic
76
+ // TODO: remove currentModalityAgnostic/SpecificSelection and use featureSelection + sampleSetSelection as the source of truth.
77
+ // Get rid of the local state within the children components and pass down the coordination values and setters directly
78
+ // so that everything stays in sync.
79
+ // E.g., if a user selects a gene from the volcano plot, the autocomplete input should reflect that selection
80
+ // and allow the user to view the corresponding gene info / gene card.
81
+ return (_jsx(_Fragment, { children: _jsx(BiomarkerSelectAlt, { setFeatureSelection: setFeatureSelection, setSampleSetFilter: setSampleSetFilter, setSampleSetSelection: setSampleSetSelection, currentModalityAgnosticSelection: currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection: setCurrentModalityAgnosticSelection, setCurrentModalitySpecificSelection: setCurrentModalitySpecificSelection, autocompleteNode: autocompleteNode, stratifications: stratificationOptions, onFinish: () => {
82
+ if (mode === 'exploratory') {
83
+ // mode is exploratory, configure accordingly.
84
+ // TODO
85
+ }
86
+ else {
87
+ // mode is confirmatory, configure accordingly.
88
+ // TODO
89
+ }
90
+ const newViewConfig = {
91
+ ...viewConfig,
92
+ coordinationSpace: {
93
+ ...viewConfig.coordinationSpace,
94
+ sampleSetFilter: {
95
+ ...viewConfig.coordinationSpace.sampleSetFilter,
96
+ __comparison__: currentStratificationSelection?.sampleSets,
97
+ },
98
+ sampleSetSelection: {
99
+ ...viewConfig.coordinationSpace.sampleSetSelection,
100
+ __comparison__: currentStratificationSelection?.sampleSets,
101
+ },
102
+ featureSelection: {
103
+ ...viewConfig.coordinationSpace.featureSelection,
104
+ __comparison__: currentModalitySpecificSelection ? currentModalitySpecificSelection.map(d => d.label) : null,
105
+ },
106
+ },
107
+ };
108
+ // TODO: can the normal coordination value setters be used instead?
109
+ // (e.g., setFeatureSelection, setSampleSetSelection)
110
+ setViewConfig(newViewConfig);
111
+ } }) }));
112
+ }
@@ -56,5 +56,5 @@ export function ComparativeHeadingSubscriber(props) {
56
56
  setFeatureSelection(null);
57
57
  }
58
58
  }, [featureSelection, setFeatureSelection]);
59
- return (_jsxs("div", { className: classes.headingContainer, children: [sampleSetSelection && sampleSetSelection.length === 2 ? (_jsxs("div", { className: classes.headingSectionContainer, children: [_jsx("div", { style: { width: '45%' }, children: _jsx("h2", { className: classes.headingText, children: sampleSetSelection?.[0]?.at(-1) }) }), _jsx("div", { style: { width: '5%' }, children: _jsx("h2", { className: classes.headingText, style: { textAlign: 'right' }, children: "vs.\u00A0" }) }), _jsx("div", { style: { width: '50%' }, children: _jsx("h2", { className: classes.headingText, children: sampleSetSelection?.[1]?.at(-1) }) }), _jsx("div", { className: classes.buttonContainer, children: _jsxs("div", { children: [_jsx("button", { onClick: swapSampleSets, type: "button", children: "Swap" }), _jsx("button", { onClick: clearSampleSetSelection, type: "button", children: "Clear" })] }) })] })) : (_jsx("div", { className: classes.headingSectionContainer, children: _jsx("div", { children: _jsx("h2", { className: classes.headingText, children: "All samples" }) }) })), featureSelection && featureSelection.length > 0 ? (_jsxs("div", { className: classes.headingSectionContainer, children: [_jsx("div", { children: _jsx("h3", { className: classes.headingText, children: "Selected Biomarkers:\u00A0" }) }), featureSelection?.map((featureName, i) => (_jsx("div", { children: _jsxs("h3", { className: classes.headingText, children: [featureName, i < featureSelection.length - 1 ? (_jsx("span", { children: ",\u00A0" })) : null] }) }, featureName))), _jsx("div", { className: classes.buttonContainer, children: _jsx("div", { children: _jsx("button", { onClick: clearFeatureSelection, type: "button", children: "Clear" }) }) })] })) : null] }));
59
+ return (_jsxs("div", { className: classes.headingContainer, children: [sampleSetSelection && sampleSetSelection.length === 2 ? (_jsxs("div", { className: classes.headingSectionContainer, children: [_jsx("div", { style: { width: '45%' }, children: _jsx("h3", { className: classes.headingText, children: sampleSetSelection?.[0]?.at(-1) }) }), _jsx("div", { style: { width: '5%' }, children: _jsx("h3", { className: classes.headingText, style: { textAlign: 'right' }, children: "vs.\u00A0" }) }), _jsx("div", { style: { width: '50%' }, children: _jsx("h3", { className: classes.headingText, children: sampleSetSelection?.[1]?.at(-1) }) }), _jsx("div", { className: classes.buttonContainer, children: _jsxs("div", { children: [_jsx("button", { onClick: swapSampleSets, type: "button", children: "Swap" }), _jsx("button", { onClick: clearSampleSetSelection, type: "button", children: "Clear" })] }) })] })) : (_jsx("div", { className: classes.headingSectionContainer, children: _jsx("div", { children: _jsx("h3", { className: classes.headingText, children: "All samples" }) }) })), featureSelection && featureSelection.length > 0 ? (_jsxs("div", { className: classes.headingSectionContainer, children: [_jsx("div", { children: _jsx("h3", { className: classes.headingText, children: "Selected Biomarkers:\u00A0" }) }), featureSelection?.map((featureName, i) => (_jsx("div", { children: _jsxs("h3", { className: classes.headingText, children: [featureName, i < featureSelection.length - 1 ? (_jsx("span", { children: ",\u00A0" })) : null] }) }, featureName))), _jsx("div", { className: classes.buttonContainer, children: _jsx("div", { children: _jsx("button", { onClick: clearFeatureSelection, type: "button", children: "Clear" }) }) })] })) : null] }));
60
60
  }
@@ -1,4 +1,5 @@
1
1
  export { BiomarkerSelectSubscriber } from "./BiomarkerSelectSubscriber.js";
2
+ export { BiomarkerSelectAltSubscriber } from "./BiomarkerSelectAltSubscriber.js";
2
3
  export { ComparativeHeadingSubscriber } from "./ComparativeHeadingSubscriber.js";
3
4
  export { SampleSetPairManagerSubscriber } from "./SampleSetPairManagerSubscriber.js";
4
5
  export { autocompleteFeature, transformFeature, getAlternativeTerms, getTermMapping } from "./default-async-functions.js";
package/dist-tsc/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { BiomarkerSelectSubscriber } from './BiomarkerSelectSubscriber.js';
2
+ export { BiomarkerSelectAltSubscriber } from './BiomarkerSelectAltSubscriber.js';
2
3
  export { ComparativeHeadingSubscriber } from './ComparativeHeadingSubscriber.js';
3
4
  export { SampleSetPairManagerSubscriber } from './SampleSetPairManagerSubscriber.js';
4
5
  export { autocompleteFeature, transformFeature, getAlternativeTerms, getTermMapping, } from './default-async-functions.js';
@@ -1 +1 @@
1
- {"version":3,"file":"select-specific.d.ts","sourceRoot":"","sources":["../src/select-specific.js"],"names":[],"mappings":"AA+BA,wDA4FC"}
1
+ {"version":3,"file":"select-specific.d.ts","sourceRoot":"","sources":["../src/select-specific.js"],"names":[],"mappings":"AA+BA,wDA6FC"}
@@ -64,6 +64,7 @@ export function SelectSpecific(props) {
64
64
  const newSelection = Array.from(newRowSelectionModel.ids)
65
65
  .map(kgId => data.find(d => d.target.kgId === kgId)?.target)
66
66
  .filter(Boolean);
67
+ // console.log('New specific selection:', newSelection);
67
68
  setCurrentModalitySpecificSelection(newSelection);
68
69
  }
69
70
  return (_jsxs(_Fragment, { children: [_jsx(Grid, { size: 6, children: _jsx(Typography, { variant: "h6", children: "Select a feature type:" }) }), _jsx(Grid, { container: true, size: 6, children: _jsx(Grid, { children: _jsxs(FormControl, { fullWidth: true, children: [_jsx(NativeSelect, { defaultValue: "gene", classes: { select: classes.selectInput }, inputProps: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitessce/biomarker-select",
3
- "version": "3.9.2",
3
+ "version": "3.9.3",
4
4
  "author": "Gehlenborg Lab",
5
5
  "homepage": "http://vitessce.io",
6
6
  "repository": {
@@ -21,10 +21,11 @@
21
21
  "fuse.js": "^7.0.0",
22
22
  "d3-dsv": "^1.1.1",
23
23
  "clsx": "^1.1.1",
24
- "@vitessce/vit-s": "3.9.2",
25
- "@vitessce/styles": "3.9.2",
26
- "@vitessce/types": "3.9.2",
27
- "@vitessce/constants-internal": "3.9.2"
24
+ "lodash-es": "^4.17.21",
25
+ "@vitessce/styles": "3.9.3",
26
+ "@vitessce/constants-internal": "3.9.3",
27
+ "@vitessce/vit-s": "3.9.3",
28
+ "@vitessce/types": "3.9.3"
28
29
  },
29
30
  "devDependencies": {
30
31
  "@testing-library/jest-dom": "^6.6.3",
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+ import { Grid } from '@vitessce/styles';
3
+ import { BiomarkerSelectAltGeneAutocomplete } from './BiomarkerSelectAltGeneAutocomplete.js';
4
+ import { BiomarkerSelectAltSampleGroups } from './BiomarkerSelectAltSampleGroups.js';
5
+ import { useStyles } from './styles.js';
6
+
7
+
8
+ export function BiomarkerSelectAlt(props) {
9
+ const {
10
+ setFeatureSelection,
11
+ setSampleSetFilter,
12
+ setSampleSetSelection,
13
+
14
+ currentModalityAgnosticSelection,
15
+ setCurrentModalityAgnosticSelection,
16
+ setCurrentModalitySpecificSelection,
17
+
18
+ autocompleteNode,
19
+
20
+ stratifications,
21
+ } = props;
22
+ const { classes } = useStyles();
23
+
24
+
25
+ return (
26
+ <Grid container sx={{ border: '0px solid red' }}>
27
+ <Grid container size={12} className={classes.header}>
28
+ <Grid container size={4} justifyContent="flex-end">
29
+ <BiomarkerSelectAltGeneAutocomplete
30
+ setFeatureSelection={setFeatureSelection}
31
+ autocompleteNode={autocompleteNode}
32
+
33
+ currentModalityAgnosticSelection={currentModalityAgnosticSelection}
34
+ setCurrentModalityAgnosticSelection={setCurrentModalityAgnosticSelection}
35
+ setCurrentModalitySpecificSelection={setCurrentModalitySpecificSelection}
36
+ />
37
+ </Grid>
38
+ <Grid container size={5} justifyContent="flex-start" flexDirection="column">
39
+ <BiomarkerSelectAltSampleGroups
40
+ setSampleSetFilter={setSampleSetFilter}
41
+ setSampleSetSelection={setSampleSetSelection}
42
+
43
+ stratifications={stratifications}
44
+ />
45
+ </Grid>
46
+ </Grid>
47
+ </Grid>
48
+ );
49
+ }
@@ -0,0 +1,103 @@
1
+ /* eslint-disable no-unused-vars */
2
+ import React, { useState } from 'react';
3
+ import {
4
+ Grid, Button,
5
+ Typography,
6
+ Dialog,
7
+ Info as InfoIcon,
8
+ SimpleAutocomplete,
9
+ } from '@vitessce/styles';
10
+ import { useStyles } from './styles.js';
11
+
12
+
13
+ export function BiomarkerSelectAltGeneAutocomplete(props) {
14
+ const {
15
+ setFeatureSelection,
16
+ autocompleteNode,
17
+ currentModalityAgnosticSelection,
18
+ setCurrentModalityAgnosticSelection,
19
+ setCurrentModalitySpecificSelection,
20
+ } = props;
21
+ const { classes } = useStyles();
22
+
23
+ const [selectedItem, setSelectedItem] = useState(null);
24
+
25
+ const [biomarkerInfoOpen, setBiomarkerInfoOpen] = useState(false);
26
+
27
+
28
+ function confirmSelectedItem() {
29
+ // eslint-disable-next-line max-len
30
+ if (selectedItem && !currentModalityAgnosticSelection?.find(item => item.kgId === selectedItem?.kgId)) {
31
+ setCurrentModalityAgnosticSelection([
32
+ ...(currentModalityAgnosticSelection || []),
33
+ selectedItem,
34
+ ]);
35
+ // TODO: directly set featureSelection coordination value here instead?
36
+ // console.log('Setting specific selection to:', selectedItem);
37
+ const nextModalitySpecificSelection = [
38
+ ...(currentModalityAgnosticSelection || []),
39
+ selectedItem,
40
+ ];
41
+ setFeatureSelection(nextModalitySpecificSelection.map(d => d.label));
42
+ }
43
+ }
44
+
45
+ function clearBiomarkerSelection() {
46
+ setCurrentModalityAgnosticSelection([]);
47
+ setCurrentModalitySpecificSelection([]);
48
+ setFeatureSelection([]);
49
+ }
50
+
51
+ return (
52
+ <>
53
+ <Grid container size={12}>
54
+ <Grid size={8}>
55
+ <SimpleAutocomplete
56
+ getMatches={autocompleteNode}
57
+ onChange={item => setSelectedItem(item.data)}
58
+ textInputLabel="Enter a gene"
59
+ getItemLabel={item => `${item.label} (${item.data.nodeType})`}
60
+ />
61
+ </Grid>
62
+ {selectedItem ? (
63
+ <Grid size={4}>
64
+ &nbsp;
65
+ <Button
66
+ variant="contained"
67
+ onClick={confirmSelectedItem}
68
+ >
69
+ Select
70
+ </Button>
71
+ </Grid>
72
+ ) : null}
73
+ </Grid>
74
+ {selectedItem ? (
75
+ <>
76
+ <Grid container size={12} flexDirection="row">
77
+ <Button onClick={() => setBiomarkerInfoOpen(true)}>
78
+ View {selectedItem.nodeType} info
79
+ </Button>
80
+ </Grid>
81
+ <Dialog open={biomarkerInfoOpen} onClose={() => setBiomarkerInfoOpen(false)} maxWidth="md">
82
+ <Typography variant="h4" title={selectedItem.term}>
83
+ {selectedItem.label}
84
+ </Typography>
85
+ <Grid container size={12}>
86
+ <InfoIcon />
87
+ <Typography variant="h6">About this {selectedItem.nodeType}</Typography>
88
+ </Grid>
89
+ <Grid container size={12} key={selectedItem.term} sx={{ width: '800px' }}>
90
+ <iframe
91
+ title={`Embedded metadata page for ontology term ${selectedItem.term}`}
92
+ src={`https://identifiers.org/${selectedItem.term}`}
93
+ width="100%"
94
+ height="500"
95
+ style={{ border: 0 }}
96
+ />
97
+ </Grid>
98
+ </Dialog>
99
+ </>
100
+ ) : null}
101
+ </>
102
+ );
103
+ }