@vitessce/biomarker-select 3.5.7 → 3.5.8

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.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import React__default, { Children, isValidElement, cloneElement, createElement, PureComponent, useRef, useEffect, useMemo, useState, useCallback } from "react";
3
- import { useCoordination, useViewConfigStoreApi, useViewConfig, useSetViewConfig, useAsyncFunction } from "@vitessce/vit-s";
3
+ import { useLoaders, useCoordination, useViewConfigStoreApi, useViewConfig, useSetViewConfig, useMatchingLoader, useColumnNameMapping, useAsyncFunction, useComparisonMetadata } from "@vitessce/vit-s";
4
4
  import * as ReactDOM from "react-dom";
5
5
  import ReactDOM__default from "react-dom";
6
6
  function getDefaultExportFromCjs(x) {
@@ -909,11 +909,36 @@ const ViewType = {
909
909
  FEATURE_VALUE_HISTOGRAM: "featureValueHistogram",
910
910
  DOT_PLOT: "dotPlot",
911
911
  FEATURE_BAR_PLOT: "featureBarPlot",
912
+ VOLCANO_PLOT: "volcanoPlot",
913
+ OBS_SET_COMPOSITION_BAR_PLOT: "obsSetCompositionBarPlot",
914
+ FEATURE_SET_ENRICHMENT_BAR_PLOT: "featureSetEnrichmentBarPlot",
912
915
  BIOMARKER_SELECT: "biomarkerSelect",
916
+ COMPARATIVE_HEADING: "comparativeHeading",
913
917
  LINK_CONTROLLER: "linkController",
918
+ NEUROGLANCER: "neuroglancer",
914
919
  DUAL_SCATTERPLOT: "dualScatterplot",
915
920
  TREEMAP: "treemap"
916
921
  };
922
+ const DataType = {
923
+ OBS_LABELS: "obsLabels",
924
+ OBS_EMBEDDING: "obsEmbedding",
925
+ OBS_FEATURE_MATRIX: "obsFeatureMatrix",
926
+ OBS_SETS: "obsSets",
927
+ FEATURE_LABELS: "featureLabels",
928
+ IMAGE: "image",
929
+ OBS_SEGMENTATIONS: "obsSegmentations",
930
+ NEIGHBORHOODS: "neighborhoods",
931
+ GENOMIC_PROFILES: "genomic-profiles",
932
+ OBS_SPOTS: "obsSpots",
933
+ OBS_POINTS: "obsPoints",
934
+ OBS_LOCATIONS: "obsLocations",
935
+ SAMPLE_SETS: "sampleSets",
936
+ SAMPLE_EDGES: "sampleEdges",
937
+ COMPARISON_METADATA: "comparisonMetadata",
938
+ FEATURE_STATS: "featureStats",
939
+ FEATURE_SET_STATS: "featureSetStats",
940
+ OBS_SET_STATS: "obsSetStats"
941
+ };
917
942
  const AsyncFunctionType = {
918
943
  // String input (rather than Node input)
919
944
  AUTOCOMPLETE_FEATURE: "autocompleteFeature",
@@ -1073,10 +1098,18 @@ const CoordinationType = {
1073
1098
  EMBEDDING_CONTOUR_PERCENTILES: "embeddingContourPercentiles",
1074
1099
  CONTOUR_COLOR_ENCODING: "contourColorEncoding",
1075
1100
  CONTOUR_COLOR: "contourColor",
1101
+ // For volcano plot:
1102
+ FEATURE_POINT_SIGNIFICANCE_THRESHOLD: "featurePointSignificanceThreshold",
1103
+ FEATURE_LABEL_SIGNIFICANCE_THRESHOLD: "featureLabelSignificanceThreshold",
1104
+ FEATURE_POINT_FOLD_CHANGE_THRESHOLD: "featurePointFoldChangeThreshold",
1105
+ FEATURE_LABEL_FOLD_CHANGE_THRESHOLD: "featureLabelFoldChangeThreshold",
1076
1106
  // Treemap
1077
1107
  HIERARCHY_LEVELS: "hierarchyLevels"
1078
1108
  };
1079
1109
  const COMPONENT_COORDINATION_TYPES = {
1110
+ [ViewType.NEUROGLANCER]: [
1111
+ CoordinationType.DATASET
1112
+ ],
1080
1113
  [ViewType.SCATTERPLOT]: [
1081
1114
  CoordinationType.DATASET,
1082
1115
  CoordinationType.OBS_TYPE,
@@ -1546,8 +1579,64 @@ const COMPONENT_COORDINATION_TYPES = {
1546
1579
  CoordinationType.OBS_COLOR_ENCODING,
1547
1580
  CoordinationType.ADDITIONAL_OBS_SETS
1548
1581
  ],
1582
+ [ViewType.VOLCANO_PLOT]: [
1583
+ CoordinationType.DATASET,
1584
+ CoordinationType.OBS_TYPE,
1585
+ CoordinationType.FEATURE_TYPE,
1586
+ CoordinationType.SAMPLE_TYPE,
1587
+ // For selection of case-control sets of samples:
1588
+ CoordinationType.SAMPLE_SET_SELECTION,
1589
+ // For selection of one-vs-others sets of observations:
1590
+ CoordinationType.OBS_SET_SELECTION,
1591
+ // TODO: CoordinationType.FEATURE_SET_SELECTION,
1592
+ // TODO: CoordinationType.FEATURE_SET_HIGHLIGHT,
1593
+ // TODO: CoordinationType.FEATURE_SET_COLOR,
1594
+ CoordinationType.FEATURE_HIGHLIGHT,
1595
+ CoordinationType.FEATURE_SELECTION,
1596
+ CoordinationType.FEATURE_VALUE_COLORMAP,
1597
+ CoordinationType.FEATURE_VALUE_COLORMAP_RANGE,
1598
+ // TODO: CoordinationType.FEATURE_COLOR_ENCODING,
1599
+ // TODO: CoordinationType.ADDITIONAL_FEATURE_SETS,
1600
+ CoordinationType.TOOLTIPS_VISIBLE,
1601
+ CoordinationType.ADDITIONAL_OBS_SETS,
1602
+ CoordinationType.OBS_SET_COLOR,
1603
+ CoordinationType.SAMPLE_SET_COLOR,
1604
+ CoordinationType.FEATURE_POINT_SIGNIFICANCE_THRESHOLD,
1605
+ CoordinationType.FEATURE_LABEL_SIGNIFICANCE_THRESHOLD,
1606
+ CoordinationType.FEATURE_POINT_FOLD_CHANGE_THRESHOLD,
1607
+ CoordinationType.FEATURE_LABEL_FOLD_CHANGE_THRESHOLD
1608
+ ],
1609
+ [ViewType.OBS_SET_COMPOSITION_BAR_PLOT]: [
1610
+ CoordinationType.DATASET,
1611
+ CoordinationType.OBS_TYPE,
1612
+ CoordinationType.SAMPLE_TYPE,
1613
+ // For selection of case-control sets of samples:
1614
+ CoordinationType.SAMPLE_SET_SELECTION,
1615
+ // For selection of one-vs-others sets of observations:
1616
+ CoordinationType.OBS_SET_SELECTION,
1617
+ CoordinationType.ADDITIONAL_OBS_SETS,
1618
+ CoordinationType.OBS_SET_COLOR,
1619
+ CoordinationType.SAMPLE_SET_COLOR
1620
+ ],
1621
+ [ViewType.FEATURE_SET_ENRICHMENT_BAR_PLOT]: [
1622
+ CoordinationType.DATASET,
1623
+ CoordinationType.OBS_TYPE,
1624
+ CoordinationType.FEATURE_TYPE,
1625
+ CoordinationType.SAMPLE_TYPE,
1626
+ // For selection of case-control sets of samples:
1627
+ CoordinationType.SAMPLE_SET_SELECTION,
1628
+ // For selection of one-vs-others sets of observations:
1629
+ CoordinationType.OBS_SET_SELECTION,
1630
+ CoordinationType.ADDITIONAL_OBS_SETS,
1631
+ CoordinationType.OBS_SET_COLOR,
1632
+ CoordinationType.SAMPLE_SET_COLOR,
1633
+ CoordinationType.FEATURE_SELECTION
1634
+ ],
1549
1635
  [ViewType.LINK_CONTROLLER]: [],
1550
1636
  [ViewType.BIOMARKER_SELECT]: [
1637
+ CoordinationType.DATASET,
1638
+ CoordinationType.OBS_TYPE,
1639
+ CoordinationType.SAMPLE_TYPE,
1551
1640
  CoordinationType.FEATURE_SELECTION,
1552
1641
  CoordinationType.SAMPLE_SET_SELECTION,
1553
1642
  CoordinationType.SAMPLE_SET_FILTER,
@@ -1555,6 +1644,16 @@ const COMPONENT_COORDINATION_TYPES = {
1555
1644
  CoordinationType.OBS_SET_FILTER
1556
1645
  // TODO: create coordination types for internal state of the biomarker selection view?
1557
1646
  ],
1647
+ [ViewType.COMPARATIVE_HEADING]: [
1648
+ CoordinationType.DATASET,
1649
+ CoordinationType.OBS_TYPE,
1650
+ CoordinationType.SAMPLE_TYPE,
1651
+ CoordinationType.FEATURE_SELECTION,
1652
+ CoordinationType.SAMPLE_SET_SELECTION,
1653
+ CoordinationType.SAMPLE_SET_FILTER,
1654
+ CoordinationType.OBS_SET_SELECTION,
1655
+ CoordinationType.OBS_SET_FILTER
1656
+ ],
1558
1657
  [ViewType.TREEMAP]: [
1559
1658
  CoordinationType.DATASET,
1560
1659
  CoordinationType.OBS_TYPE,
@@ -30769,7 +30868,7 @@ function SelectStratification(props) {
30769
30868
  ) })
30770
30869
  ] });
30771
30870
  }
30772
- const steps = [
30871
+ const steps$1 = [
30773
30872
  "Select biomarkers (modality-agnostic)",
30774
30873
  "Derive modality-specific biomarker signature",
30775
30874
  "Define case-control stratification"
@@ -30819,12 +30918,12 @@ function ConfirmatoryStepper(props) {
30819
30918
  setActiveStep(0);
30820
30919
  };
30821
30920
  useEffect(() => {
30822
- if (activeStep === steps.length) {
30921
+ if (activeStep === steps$1.length) {
30823
30922
  onFinish();
30824
30923
  }
30825
- }, [activeStep, steps]);
30924
+ }, [activeStep, steps$1]);
30826
30925
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(Grid$1, { item: true, container: true, xs: 12, children: [
30827
- /* @__PURE__ */ jsxRuntimeExports.jsx(Grid$1, { item: true, container: true, xs: 12, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Box$1, { className: classes.fullWidthBox, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Stepper$1, { activeStep, children: steps.map((label, index) => {
30926
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Grid$1, { item: true, container: true, xs: 12, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Box$1, { className: classes.fullWidthBox, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Stepper$1, { activeStep, children: steps$1.map((label, index) => {
30828
30927
  const stepProps = {};
30829
30928
  const labelProps = {};
30830
30929
  if (isStepOptional(index)) {
@@ -30836,7 +30935,7 @@ function ConfirmatoryStepper(props) {
30836
30935
  return /* @__PURE__ */ jsxRuntimeExports.jsx(Step$1, { ...stepProps, children: /* @__PURE__ */ jsxRuntimeExports.jsx(StepLabel$1, { ...labelProps, children: label }) }, label);
30837
30936
  }) }) }) }),
30838
30937
  /* @__PURE__ */ jsxRuntimeExports.jsxs(Grid$1, { item: true, container: true, xs: 12, style: { marginTop: "20px" }, alignItems: "flex-start", children: [
30839
- /* @__PURE__ */ jsxRuntimeExports.jsx(Grid$1, { item: true, container: true, xs: 8, children: activeStep === steps.length ? /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Typography$1, { style: { marginTop: "20px", marginBottom: "10px" }, children: "All steps completed - you're finished" }) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
30938
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Grid$1, { item: true, container: true, xs: 8, children: activeStep === steps$1.length ? /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Typography$1, { style: { marginTop: "20px", marginBottom: "10px" }, children: "All steps completed - you're finished" }) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
30840
30939
  activeStep === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(
30841
30940
  SelectAgnostic,
30842
30941
  {
@@ -30892,6 +30991,118 @@ function ConfirmatoryStepper(props) {
30892
30991
  stratifications
30893
30992
  }
30894
30993
  ) }),
30994
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Box$1, { className: classes.fullWidthBox, children: activeStep === steps$1.length ? /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Box$1, { style: { display: "flex", flexDirection: "row", paddingTop: "20px" }, children: [
30995
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Box$1, { style: { flex: "1 1 auto" } }),
30996
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Button$1, { onClick: handleReset, children: "Reset" })
30997
+ ] }) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(Box$1, { style: { display: "flex", flexDirection: "row", paddingTop: "20px" }, children: [
30998
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
30999
+ Button$1,
31000
+ {
31001
+ color: "inherit",
31002
+ disabled: activeStep === 0,
31003
+ onClick: handleBack,
31004
+ style: { mr: 1 },
31005
+ children: "Back"
31006
+ }
31007
+ ),
31008
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Box$1, { style: { flex: "1 1 auto" } }),
31009
+ isStepOptional(activeStep) && /* @__PURE__ */ jsxRuntimeExports.jsx(Button$1, { color: "inherit", onClick: handleSkip, style: { marginRight: "10px" }, children: "Skip" }),
31010
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Button$1, { onClick: handleNext, children: activeStep === steps$1.length - 1 ? "Finish" : "Next" })
31011
+ ] }) })
31012
+ ] })
31013
+ ] });
31014
+ }
31015
+ const steps = [
31016
+ "Define case-control stratification"
31017
+ ];
31018
+ function ExploratoryStepper(props) {
31019
+ const {
31020
+ autocompleteNode,
31021
+ currentModalityAgnosticSelection,
31022
+ setCurrentModalityAgnosticSelection,
31023
+ currentModalitySpecificSelection,
31024
+ setCurrentModalitySpecificSelection,
31025
+ currentStratificationSelection,
31026
+ setCurrentStratificationSelection,
31027
+ getEdges,
31028
+ stratifications,
31029
+ onFinish
31030
+ } = props;
31031
+ const classes = useStyles();
31032
+ const [activeStep, setActiveStep] = React__default.useState(0);
31033
+ const [skipped, setSkipped] = React__default.useState(/* @__PURE__ */ new Set());
31034
+ const isStepOptional = (step) => step === 2;
31035
+ const isStepSkipped = (step) => skipped.has(step);
31036
+ const handleNext = () => {
31037
+ let newSkipped = skipped;
31038
+ if (isStepSkipped(activeStep)) {
31039
+ newSkipped = new Set(newSkipped.values());
31040
+ newSkipped.delete(activeStep);
31041
+ }
31042
+ setActiveStep((prevActiveStep) => prevActiveStep + 1);
31043
+ setSkipped(newSkipped);
31044
+ };
31045
+ const handleBack = () => {
31046
+ setActiveStep((prevActiveStep) => prevActiveStep - 1);
31047
+ };
31048
+ const handleSkip = () => {
31049
+ if (!isStepOptional(activeStep)) {
31050
+ throw new Error("You can't skip a step that isn't optional.");
31051
+ }
31052
+ setActiveStep((prevActiveStep) => prevActiveStep + 1);
31053
+ setSkipped((prevSkipped) => {
31054
+ const newSkipped = new Set(prevSkipped.values());
31055
+ newSkipped.add(activeStep);
31056
+ return newSkipped;
31057
+ });
31058
+ };
31059
+ const handleReset = () => {
31060
+ setActiveStep(0);
31061
+ };
31062
+ useEffect(() => {
31063
+ if (activeStep === steps.length) {
31064
+ onFinish();
31065
+ }
31066
+ }, [activeStep, steps]);
31067
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(Grid$1, { item: true, container: true, xs: 12, children: [
31068
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Grid$1, { item: true, container: true, xs: 12, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Box$1, { className: classes.fullWidthBox, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Stepper$1, { activeStep, children: steps.map((label, index) => {
31069
+ const stepProps = {};
31070
+ const labelProps = {};
31071
+ if (isStepOptional(index)) {
31072
+ labelProps.optional = /* @__PURE__ */ jsxRuntimeExports.jsx(Typography$1, { variant: "caption", children: "Optional" });
31073
+ }
31074
+ if (isStepSkipped(index)) {
31075
+ stepProps.completed = false;
31076
+ }
31077
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Step$1, { ...stepProps, children: /* @__PURE__ */ jsxRuntimeExports.jsx(StepLabel$1, { ...labelProps, children: label }) }, label);
31078
+ }) }) }) }),
31079
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Grid$1, { item: true, container: true, xs: 12, style: { marginTop: "20px" }, alignItems: "flex-start", children: [
31080
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Grid$1, { item: true, container: true, xs: 8, children: activeStep === steps.length ? /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Typography$1, { style: { marginTop: "20px", marginBottom: "10px" }, children: "All steps completed - you're finished" }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: activeStep === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(
31081
+ SelectStratification,
31082
+ {
31083
+ autocompleteNode,
31084
+ currentModalityAgnosticSelection,
31085
+ setCurrentModalityAgnosticSelection,
31086
+ currentModalitySpecificSelection,
31087
+ setCurrentModalitySpecificSelection,
31088
+ currentStratificationSelection,
31089
+ setCurrentStratificationSelection,
31090
+ getEdges,
31091
+ stratifications
31092
+ }
31093
+ ) : null }) }),
31094
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Grid$1, { item: true, container: true, xs: 4, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
31095
+ ConfirmatoryCart,
31096
+ {
31097
+ currentModalityAgnosticSelection,
31098
+ setCurrentModalityAgnosticSelection,
31099
+ currentModalitySpecificSelection,
31100
+ setCurrentModalitySpecificSelection,
31101
+ currentStratificationSelection,
31102
+ setCurrentStratificationSelection,
31103
+ stratifications
31104
+ }
31105
+ ) }),
30895
31106
  /* @__PURE__ */ jsxRuntimeExports.jsx(Box$1, { className: classes.fullWidthBox, children: activeStep === steps.length ? /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Box$1, { style: { display: "flex", flexDirection: "row", paddingTop: "20px" }, children: [
30896
31107
  /* @__PURE__ */ jsxRuntimeExports.jsx(Box$1, { style: { flex: "1 1 auto" } }),
30897
31108
  /* @__PURE__ */ jsxRuntimeExports.jsx(Button$1, { onClick: handleReset, children: "Reset" })
@@ -30958,22 +31169,60 @@ function ScmdUi(props) {
30958
31169
  onFinish
30959
31170
  }
30960
31171
  ) : null,
30961
- mode === "exploratory" ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "TODO" }) : null
31172
+ mode === "exploratory" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
31173
+ ExploratoryStepper,
31174
+ {
31175
+ autocompleteNode,
31176
+ currentModalityAgnosticSelection,
31177
+ setCurrentModalityAgnosticSelection,
31178
+ currentModalitySpecificSelection,
31179
+ setCurrentModalitySpecificSelection,
31180
+ currentStratificationSelection,
31181
+ setCurrentStratificationSelection,
31182
+ getEdges,
31183
+ stratifications,
31184
+ onFinish
31185
+ }
31186
+ ) : null
30962
31187
  ] }) : null
30963
31188
  ] });
30964
31189
  }
30965
31190
  function BiomarkerSelectSubscriber(props) {
30966
31191
  const {
30967
31192
  coordinationScopes,
30968
- stratificationOptions
31193
+ stratificationOptions: stratificationOptionsProp
31194
+ // TODO: Remove; Get from comparisonMetadata instead
30969
31195
  } = props;
30970
- useCoordination(
31196
+ const loaders = useLoaders();
31197
+ const [{
31198
+ dataset,
31199
+ obsType,
31200
+ sampleType,
31201
+ sampleSetSelection,
31202
+ featureSelection
31203
+ }, {
31204
+ setSampleSetSelection,
31205
+ setFeatureSelection
31206
+ }] = useCoordination(
30971
31207
  COMPONENT_COORDINATION_TYPES[ViewType.BIOMARKER_SELECT],
30972
31208
  coordinationScopes
30973
31209
  );
30974
31210
  const viewConfigStoreApi = useViewConfigStoreApi();
30975
31211
  const viewConfig = useViewConfig();
30976
31212
  const setViewConfig = useSetViewConfig(viewConfigStoreApi);
31213
+ useMatchingLoader(
31214
+ loaders,
31215
+ dataset,
31216
+ DataType.OBS_SETS,
31217
+ { obsType }
31218
+ );
31219
+ const sampleSetsLoader = useMatchingLoader(
31220
+ loaders,
31221
+ dataset,
31222
+ DataType.SAMPLE_SETS,
31223
+ { sampleType }
31224
+ );
31225
+ const sampleSetsColumnNameMappingReversed = useColumnNameMapping(sampleSetsLoader, true);
30977
31226
  const [isSelecting, setIsSelecting] = useState(true);
30978
31227
  const [mode, setMode] = useState(null);
30979
31228
  const [step, setStep] = useState(null);
@@ -30990,6 +31239,36 @@ function BiomarkerSelectSubscriber(props) {
30990
31239
  async (node, targetModality) => transformFeature2(node, targetModality),
30991
31240
  [transformFeature2]
30992
31241
  );
31242
+ const [{ comparisonMetadata }, cmpMetadataStatus, cmpMetadataUrls] = useComparisonMetadata(
31243
+ loaders,
31244
+ dataset,
31245
+ false,
31246
+ {},
31247
+ {},
31248
+ { obsType, sampleType }
31249
+ );
31250
+ const stratificationOptions = useMemo(() => {
31251
+ if (comparisonMetadata == null ? void 0 : comparisonMetadata.sample_group_pairs) {
31252
+ return comparisonMetadata.sample_group_pairs.map((sampleGroupPair) => {
31253
+ const [sampleGroupCol, sampleGroupValues] = sampleGroupPair;
31254
+ const [sampleGroupCtrl, sampleGroupCase] = sampleGroupValues;
31255
+ const groupName = sampleSetsColumnNameMappingReversed == null ? void 0 : sampleSetsColumnNameMappingReversed[sampleGroupCol];
31256
+ return {
31257
+ stratificationId: `${sampleGroupCol}_${sampleGroupCtrl}-vs-${sampleGroupCase}`,
31258
+ name: `${groupName}: ${sampleGroupCtrl} vs. ${sampleGroupCase}`,
31259
+ stratificationType: "sampleSet",
31260
+ sampleSets: [
31261
+ // With sampleSets coming from the comparison_metadata,
31262
+ // need to use loader options from obsSets and sampleSets to get mapping
31263
+ // from column name to group name.
31264
+ [groupName, sampleGroupCtrl],
31265
+ [groupName, sampleGroupCase]
31266
+ ]
31267
+ };
31268
+ });
31269
+ }
31270
+ return null;
31271
+ }, [comparisonMetadata, sampleSetsColumnNameMappingReversed]);
30993
31272
  return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: isSelecting ? /* @__PURE__ */ jsxRuntimeExports.jsx(
30994
31273
  ScmdUi,
30995
31274
  {
@@ -31007,24 +31286,46 @@ function BiomarkerSelectSubscriber(props) {
31007
31286
  getEdges,
31008
31287
  stratifications: stratificationOptions,
31009
31288
  onFinish: () => {
31010
- setViewConfig({
31289
+ const newViewConfig = {
31011
31290
  ...viewConfig,
31012
31291
  coordinationSpace: {
31013
31292
  ...viewConfig.coordinationSpace,
31014
31293
  sampleSetFilter: {
31015
31294
  ...viewConfig.coordinationSpace.sampleSetFilter,
31016
- "case-control": currentStratificationSelection == null ? void 0 : currentStratificationSelection.sampleSets
31295
+ __comparison__: currentStratificationSelection == null ? void 0 : currentStratificationSelection.sampleSets
31017
31296
  },
31018
31297
  sampleSetSelection: {
31019
31298
  ...viewConfig.coordinationSpace.sampleSetSelection,
31020
- "case-control": currentStratificationSelection == null ? void 0 : currentStratificationSelection.sampleSets
31299
+ __comparison__: currentStratificationSelection == null ? void 0 : currentStratificationSelection.sampleSets
31300
+ },
31301
+ featureSelection: {
31302
+ ...viewConfig.coordinationSpace.featureSelection,
31303
+ __comparison__: currentModalitySpecificSelection ? currentModalitySpecificSelection.map((d) => d.label) : null
31021
31304
  }
31022
31305
  }
31023
- });
31306
+ };
31307
+ setViewConfig(newViewConfig);
31024
31308
  }
31025
31309
  }
31026
31310
  ) : null });
31027
31311
  }
31312
+ function ComparativeHeadingSubscriber(props) {
31313
+ var _a2, _b;
31314
+ const {
31315
+ coordinationScopes
31316
+ } = props;
31317
+ const [{
31318
+ sampleSetSelection
31319
+ }] = useCoordination(
31320
+ COMPONENT_COORDINATION_TYPES[ViewType.COMPARATIVE_HEADING],
31321
+ coordinationScopes
31322
+ );
31323
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: sampleSetSelection && sampleSetSelection.length === 2 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { width: "100%", display: "flex", flexDirection: "row" }, children: [
31324
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { width: "45%" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { children: (_a2 = sampleSetSelection == null ? void 0 : sampleSetSelection[0]) == null ? void 0 : _a2.at(-1) }) }),
31325
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { width: "5%" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { style: { textAlign: "right" }, children: "vs. " }) }),
31326
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { width: "50%" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { children: (_b = sampleSetSelection == null ? void 0 : sampleSetSelection[1]) == null ? void 0 : _b.at(-1) }) })
31327
+ ] }) : null });
31328
+ }
31028
31329
  var EOL = {}, EOF = {}, QUOTE = 34, NEWLINE = 10, RETURN = 13;
31029
31330
  function objectConverter(columns2) {
31030
31331
  return new Function("d", "return {" + columns2.map(function(name, i) {
@@ -32223,6 +32524,7 @@ async function getTermMapping({ queryClient }, keyCuriePrefix, valCuriePrefix) {
32223
32524
  }
32224
32525
  export {
32225
32526
  BiomarkerSelectSubscriber,
32527
+ ComparativeHeadingSubscriber,
32226
32528
  autocompleteFeature,
32227
32529
  getAlternativeTerms,
32228
32530
  getTermMapping,
@@ -1 +1 @@
1
- {"version":3,"file":"BiomarkerSelectSubscriber.d.ts","sourceRoot":"","sources":["../src/BiomarkerSelectSubscriber.js"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,iDAHG;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,eAmFF"}
1
+ {"version":3,"file":"BiomarkerSelectSubscriber.d.ts","sourceRoot":"","sources":["../src/BiomarkerSelectSubscriber.js"],"names":[],"mappings":"AAiBA;;;;;GAKG;AACH,iDAHG;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,eA0JF"}
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  /* eslint-disable max-len */
3
3
  /* eslint-disable no-unused-vars */
4
- import React, { useState, useCallback } from 'react';
5
- import { useAsyncFunction, useViewConfigStoreApi, useSetViewConfig, useViewConfig, useCoordination, } from '@vitessce/vit-s';
6
- import { AsyncFunctionType, ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
4
+ import React, { useState, useCallback, useMemo } from 'react';
5
+ import { useAsyncFunction, useViewConfigStoreApi, useSetViewConfig, useViewConfig, useCoordination, useLoaders, useComparisonMetadata, useMatchingLoader, useColumnNameMapping, } from '@vitessce/vit-s';
6
+ import { AsyncFunctionType, ViewType, COMPONENT_COORDINATION_TYPES, DataType } from '@vitessce/constants-internal';
7
7
  import { ScmdUi } from './scmd-ui.js';
8
8
  /**
9
9
  *
@@ -12,11 +12,18 @@ import { ScmdUi } from './scmd-ui.js';
12
12
  * @returns
13
13
  */
14
14
  export function BiomarkerSelectSubscriber(props) {
15
- const { coordinationScopes, stratificationOptions, } = props;
16
- const [{ sampleSetSelection, featureSelection, }, { setSampleSetSelection, setFeatureSelection, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.BIOMARKER_SELECT], coordinationScopes);
15
+ const { coordinationScopes, stratificationOptions: stratificationOptionsProp, // TODO: Remove; Get from comparisonMetadata instead
16
+ } = props;
17
+ const loaders = useLoaders();
18
+ const [{ dataset, obsType, sampleType, sampleSetSelection, featureSelection, }, { setSampleSetSelection, setFeatureSelection, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.BIOMARKER_SELECT], coordinationScopes);
17
19
  const viewConfigStoreApi = useViewConfigStoreApi();
18
20
  const viewConfig = useViewConfig();
19
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);
20
27
  // TODO: make isSelecting a coordination type plugin.
21
28
  // TODO: use store hooks from @vitessce/vit-s to update the view config based on the selections.
22
29
  const [isSelecting, setIsSelecting] = useState(true);
@@ -29,20 +36,69 @@ export function BiomarkerSelectSubscriber(props) {
29
36
  const transformFeature = useAsyncFunction(AsyncFunctionType.TRANSFORM_FEATURE);
30
37
  const autocompleteNode = useCallback(async (inputValue) => autocompleteFeature(inputValue), [autocompleteFeature]);
31
38
  const getEdges = useCallback(async (node, targetModality) => transformFeature(node, targetModality), [transformFeature]);
39
+ const [{ comparisonMetadata }, cmpMetadataStatus, cmpMetadataUrls] = useComparisonMetadata(loaders, dataset, false, {}, {}, { obsType, sampleType });
40
+ const stratificationOptions = useMemo(() => {
41
+ /*
42
+ return array of objects like {
43
+ stratificationId: 'aki-vs-hr',
44
+ name: 'Acute kidney injury (AKI) vs. Healthy reference',
45
+ stratificationType: 'sampleSet', // key changed from 'groupType'. value changed from 'clinical'
46
+ sampleSets: [
47
+ ['Disease Type', 'AKI'],
48
+ ['Disease Type', 'Reference'],
49
+ ],
50
+ },
51
+ */
52
+ if (comparisonMetadata?.sample_group_pairs) {
53
+ return comparisonMetadata.sample_group_pairs.map((sampleGroupPair) => {
54
+ const [sampleGroupCol, sampleGroupValues] = sampleGroupPair;
55
+ const [sampleGroupCtrl, sampleGroupCase] = sampleGroupValues;
56
+ const groupName = sampleSetsColumnNameMappingReversed?.[sampleGroupCol];
57
+ return {
58
+ stratificationId: `${sampleGroupCol}_${sampleGroupCtrl}-vs-${sampleGroupCase}`,
59
+ name: `${groupName}: ${sampleGroupCtrl} vs. ${sampleGroupCase}`,
60
+ stratificationType: 'sampleSet',
61
+ sampleSets: [
62
+ // With sampleSets coming from the comparison_metadata,
63
+ // need to use loader options from obsSets and sampleSets to get mapping
64
+ // from column name to group name.
65
+ [groupName, sampleGroupCtrl],
66
+ [groupName, sampleGroupCase],
67
+ ],
68
+ };
69
+ });
70
+ }
71
+ return null;
72
+ }, [comparisonMetadata, sampleSetsColumnNameMappingReversed]);
32
73
  return (_jsx(_Fragment, { children: isSelecting ? (_jsx(ScmdUi, { mode: mode, setMode: setMode, step: step, setStep: setStep, currentModalityAgnosticSelection: currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection: setCurrentModalityAgnosticSelection, currentModalitySpecificSelection: currentModalitySpecificSelection, setCurrentModalitySpecificSelection: setCurrentModalitySpecificSelection, currentStratificationSelection: currentStratificationSelection, setCurrentStratificationSelection: setCurrentStratificationSelection, autocompleteNode: autocompleteNode, getEdges: getEdges, stratifications: stratificationOptions, onFinish: () => {
33
- setViewConfig({
74
+ if (mode === 'exploratory') {
75
+ // mode is exploratory, configure accordingly.
76
+ // TODO
77
+ }
78
+ else {
79
+ // mode is confirmatory, configure accordingly.
80
+ // TODO
81
+ }
82
+ const newViewConfig = {
34
83
  ...viewConfig,
35
84
  coordinationSpace: {
36
85
  ...viewConfig.coordinationSpace,
37
86
  sampleSetFilter: {
38
87
  ...viewConfig.coordinationSpace.sampleSetFilter,
39
- 'case-control': currentStratificationSelection?.sampleSets,
88
+ __comparison__: currentStratificationSelection?.sampleSets,
40
89
  },
41
90
  sampleSetSelection: {
42
91
  ...viewConfig.coordinationSpace.sampleSetSelection,
43
- 'case-control': currentStratificationSelection?.sampleSets,
92
+ __comparison__: currentStratificationSelection?.sampleSets,
93
+ },
94
+ featureSelection: {
95
+ ...viewConfig.coordinationSpace.featureSelection,
96
+ __comparison__: currentModalitySpecificSelection ? currentModalitySpecificSelection.map(d => d.label) : null,
44
97
  },
45
98
  },
46
- });
99
+ };
100
+ // TODO: can the normal coordination value setters be used instead?
101
+ // (e.g., setFeatureSelection, setSampleSetSelection)
102
+ setViewConfig(newViewConfig);
47
103
  } })) : null }));
48
104
  }
@@ -0,0 +1,2 @@
1
+ export function ComparativeHeadingSubscriber(props: any): JSX.Element;
2
+ //# sourceMappingURL=ComparativeHeadingSubscriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComparativeHeadingSubscriber.d.ts","sourceRoot":"","sources":["../src/ComparativeHeadingSubscriber.js"],"names":[],"mappings":"AAOA,sEAuBC"}
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { useCoordination, } from '@vitessce/vit-s';
4
+ import { ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
5
+ export function ComparativeHeadingSubscriber(props) {
6
+ const { coordinationScopes, } = props;
7
+ const [{ sampleSetSelection, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.COMPARATIVE_HEADING], coordinationScopes);
8
+ return (_jsx(_Fragment, { children: sampleSetSelection && sampleSetSelection.length === 2 ? (_jsxs("div", { style: { width: '100%', display: 'flex', flexDirection: 'row' }, children: [_jsx("div", { style: { width: '45%' }, children: _jsx("h2", { children: sampleSetSelection?.[0]?.at(-1) }) }), _jsx("div", { style: { width: '5%' }, children: _jsx("h2", { style: { textAlign: 'right' }, children: "vs.\u00A0" }) }), _jsx("div", { style: { width: '50%' }, children: _jsx("h2", { children: sampleSetSelection?.[1]?.at(-1) }) })] })) : null }));
9
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"default-async-functions.d.ts","sourceRoot":"","sources":["../src/default-async-functions.js"],"names":[],"mappings":"AAqHA;;;;;;;GAOG;AACH,qDALG;IAAyB,WAAW,EAA5B,WAAW;CACnB,WAAQ,MAAM,kBACN,IAAI,GAAG,kBAAkB,GACvB,OAAO,CAAC,MAAM,EAAE,CAAC,CAiB7B;AAeD;;;;;;;GAOG;AACH,kDALG;IAAyB,WAAW,EAA5B,WAAW;CACnB,QAAQ,MAAM,kBACN,kBAAkB,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC,CAkC7B;AAED;;;;;;GAMG;AACH,qDAJG;IAAyB,WAAW,EAA5B,WAAW;CACnB,SAAQ,MAAM,GACJ,OAAO,CAAC,MAAM,EAAE,CAAC,CA2B7B;AAED;;;;;;;GAOG;AACH,gDALG;IAAyB,WAAW,EAA5B,WAAW;CACnB,kBAAQ,MAAM,kBACN,MAAM,GACJ,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAoBxC;iCAzPsD,uBAAuB;wCADsE,iBAAiB;4BAAjB,iBAAiB"}
1
+ {"version":3,"file":"default-async-functions.d.ts","sourceRoot":"","sources":["../src/default-async-functions.js"],"names":[],"mappings":"AAqHA;;;;;;;GAOG;AACH,qDALG;IAAyB,WAAW,EAA5B,WAAW;CACnB,WAAQ,MAAM,kBACN,IAAI,GAAG,kBAAkB,GACvB,OAAO,CAAC,MAAM,EAAE,CAAC,CAiB7B;AAeD;;;;;;;GAOG;AACH,kDALG;IAAyB,WAAW,EAA5B,WAAW;CACnB,QAAQ,MAAM,kBACN,kBAAkB,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC,CAmC7B;AAED;;;;;;GAMG;AACH,qDAJG;IAAyB,WAAW,EAA5B,WAAW;CACnB,SAAQ,MAAM,GACJ,OAAO,CAAC,MAAM,EAAE,CAAC,CA2B7B;AAED;;;;;;;GAOG;AACH,gDALG;IAAyB,WAAW,EAA5B,WAAW;CACnB,kBAAQ,MAAM,kBACN,MAAM,GACJ,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAoBxC;iCA1PsD,uBAAuB;wCADsE,iBAAiB;4BAAjB,iBAAiB"}
@@ -161,6 +161,7 @@ export async function transformFeature({ queryClient }, node, targetModality) {
161
161
  staleTime: Infinity,
162
162
  queryFn: loadPathwayToGeneEdges,
163
163
  });
164
+ // TODO: support matching using ontology term (rather than requiring kgId)?
164
165
  const matchingEdges = pathwayGeneEdges.filter((/** @type {KgEdge} */ d) => d.source === node.kgId);
165
166
  const matchingGeneIds = matchingEdges.map((/** @type {KgEdge} */ d) => d.target);
166
167
  const matchingGenes = geneNodes.filter(d => matchingGeneIds.includes(d.kgId));
@@ -1,3 +1,4 @@
1
1
  export { BiomarkerSelectSubscriber } from "./BiomarkerSelectSubscriber.js";
2
+ export { ComparativeHeadingSubscriber } from "./ComparativeHeadingSubscriber.js";
2
3
  export { autocompleteFeature, transformFeature, getAlternativeTerms, getTermMapping } from "./default-async-functions.js";
3
4
  //# sourceMappingURL=index.d.ts.map
package/dist-tsc/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { BiomarkerSelectSubscriber } from './BiomarkerSelectSubscriber.js';
2
+ export { ComparativeHeadingSubscriber } from './ComparativeHeadingSubscriber.js';
2
3
  export { autocompleteFeature, transformFeature, getAlternativeTerms, getTermMapping, } from './default-async-functions.js';
@@ -1 +1 @@
1
- {"version":3,"file":"scmd-ui.d.ts","sourceRoot":"","sources":["../src/scmd-ui.js"],"names":[],"mappings":"AAOA,gDAmEC"}
1
+ {"version":3,"file":"scmd-ui.d.ts","sourceRoot":"","sources":["../src/scmd-ui.js"],"names":[],"mappings":"AAQA,gDAkFC"}
@@ -2,7 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /* eslint-disable no-unused-vars */
3
3
  import React, { useState } from 'react';
4
4
  import { Button, ButtonGroup, Grid, Tooltip } from '@material-ui/core';
5
- import { ConfirmatoryStepper } from './confirmatory-stepper.js';
5
+ import { ConfirmatoryStepper } from './stepper-confirmatory.js';
6
+ import { ExploratoryStepper } from './stepper-exploratory.js';
6
7
  import { useStyles } from './styles.js';
7
8
  export function ScmdUi(props) {
8
9
  const { mode: modeProp, setMode, step: stepProp, setStep, currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection, currentModalitySpecificSelection, setCurrentModalitySpecificSelection, currentStratificationSelection, setCurrentStratificationSelection, autocompleteNode, getEdges, stratifications, onFinish, } = props;
@@ -11,5 +12,5 @@ export function ScmdUi(props) {
11
12
  const step = mode === 'confirmatory' ? (stepProp || 'select-biomarkers') : (stepProp || 'define-stratification');
12
13
  const [tempIsVisible, setTempIsVisible] = useState(true);
13
14
  const isConfirmatoryMode = mode === 'confirmatory';
14
- return (_jsxs(Grid, { container: true, style: { border: '0px solid red' }, children: [_jsxs(Grid, { item: true, container: true, xs: 12, className: classes.header, children: [_jsx(Grid, { item: true, xs: 8 }), _jsx(Grid, { item: true, container: true, xs: 4, justifyContent: "flex-end", children: _jsxs(ButtonGroup, { variant: "outlined", size: "small", color: "secondary", "aria-label": "Toggle between confirmatory and exploratory modes", children: [_jsx(Tooltip, { arrow: true, title: "Start from biomarker(s) of interest", children: _jsx(Button, { variant: isConfirmatoryMode ? 'contained' : 'outlined', onClick: () => setMode('confirmatory'), children: "Confirmatory (Hypothesis-driven)" }) }), _jsx(Tooltip, { arrow: true, title: "Lacking biomarker(s) of interest", children: _jsx(Button, { variant: !isConfirmatoryMode ? 'contained' : 'outlined', onClick: () => setMode('exploratory'), children: "Exploratory (Hypothesis-generating)" }) })] }) })] }), tempIsVisible ? (_jsxs("div", { style: { border: '2px solid #eee', borderRadius: '5px' }, children: [mode === 'confirmatory' ? (_jsx(ConfirmatoryStepper, { autocompleteNode: autocompleteNode, currentModalityAgnosticSelection: currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection: setCurrentModalityAgnosticSelection, currentModalitySpecificSelection: currentModalitySpecificSelection, setCurrentModalitySpecificSelection: setCurrentModalitySpecificSelection, currentStratificationSelection: currentStratificationSelection, setCurrentStratificationSelection: setCurrentStratificationSelection, getEdges: getEdges, stratifications: stratifications, onFinish: onFinish })) : null, mode === 'exploratory' ? _jsx("span", { children: "TODO" }) : null] })) : null] }));
15
+ return (_jsxs(Grid, { container: true, style: { border: '0px solid red' }, children: [_jsxs(Grid, { item: true, container: true, xs: 12, className: classes.header, children: [_jsx(Grid, { item: true, xs: 8 }), _jsx(Grid, { item: true, container: true, xs: 4, justifyContent: "flex-end", children: _jsxs(ButtonGroup, { variant: "outlined", size: "small", color: "secondary", "aria-label": "Toggle between confirmatory and exploratory modes", children: [_jsx(Tooltip, { arrow: true, title: "Start from biomarker(s) of interest", children: _jsx(Button, { variant: isConfirmatoryMode ? 'contained' : 'outlined', onClick: () => setMode('confirmatory'), children: "Confirmatory (Hypothesis-driven)" }) }), _jsx(Tooltip, { arrow: true, title: "Lacking biomarker(s) of interest", children: _jsx(Button, { variant: !isConfirmatoryMode ? 'contained' : 'outlined', onClick: () => setMode('exploratory'), children: "Exploratory (Hypothesis-generating)" }) })] }) })] }), tempIsVisible ? (_jsxs("div", { style: { border: '2px solid #eee', borderRadius: '5px' }, children: [mode === 'confirmatory' ? (_jsx(ConfirmatoryStepper, { autocompleteNode: autocompleteNode, currentModalityAgnosticSelection: currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection: setCurrentModalityAgnosticSelection, currentModalitySpecificSelection: currentModalitySpecificSelection, setCurrentModalitySpecificSelection: setCurrentModalitySpecificSelection, currentStratificationSelection: currentStratificationSelection, setCurrentStratificationSelection: setCurrentStratificationSelection, getEdges: getEdges, stratifications: stratifications, onFinish: onFinish })) : null, mode === 'exploratory' ? (_jsx(ExploratoryStepper, { autocompleteNode: autocompleteNode, currentModalityAgnosticSelection: currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection: setCurrentModalityAgnosticSelection, currentModalitySpecificSelection: currentModalitySpecificSelection, setCurrentModalitySpecificSelection: setCurrentModalitySpecificSelection, currentStratificationSelection: currentStratificationSelection, setCurrentStratificationSelection: setCurrentStratificationSelection, getEdges: getEdges, stratifications: stratifications, onFinish: onFinish })) : null] })) : null] }));
15
16
  }
@@ -1,2 +1,2 @@
1
1
  export function ConfirmatoryStepper(props: any): JSX.Element;
2
- //# sourceMappingURL=confirmatory-stepper.d.ts.map
2
+ //# sourceMappingURL=stepper-confirmatory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stepper-confirmatory.d.ts","sourceRoot":"","sources":["../src/stepper-confirmatory.js"],"names":[],"mappings":"AAcA,6DAkMC"}
@@ -0,0 +1,2 @@
1
+ export function ExploratoryStepper(props: any): JSX.Element;
2
+ //# sourceMappingURL=stepper-exploratory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stepper-exploratory.d.ts","sourceRoot":"","sources":["../src/stepper-exploratory.js"],"names":[],"mappings":"AAUA,4DAoKC"}
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useEffect } from 'react';
3
+ import { Stepper, Step, StepLabel, Button, Grid, Box, Typography } from '@material-ui/core';
4
+ import { ConfirmatoryCart } from './cart.js';
5
+ import { SelectStratification } from './select-stratification.js';
6
+ import { useStyles } from './styles.js';
7
+ const steps = [
8
+ 'Define case-control stratification',
9
+ ];
10
+ export function ExploratoryStepper(props) {
11
+ const { autocompleteNode, currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection, currentModalitySpecificSelection, setCurrentModalitySpecificSelection, currentStratificationSelection, setCurrentStratificationSelection, getEdges, stratifications, onFinish, } = props;
12
+ const classes = useStyles();
13
+ const [activeStep, setActiveStep] = React.useState(0);
14
+ const [skipped, setSkipped] = React.useState(new Set());
15
+ const isStepOptional = step => step === 2;
16
+ const isStepSkipped = step => skipped.has(step);
17
+ const handleNext = () => {
18
+ let newSkipped = skipped;
19
+ if (isStepSkipped(activeStep)) {
20
+ newSkipped = new Set(newSkipped.values());
21
+ newSkipped.delete(activeStep);
22
+ }
23
+ setActiveStep(prevActiveStep => prevActiveStep + 1);
24
+ setSkipped(newSkipped);
25
+ };
26
+ const handleBack = () => {
27
+ setActiveStep(prevActiveStep => prevActiveStep - 1);
28
+ };
29
+ const handleSkip = () => {
30
+ if (!isStepOptional(activeStep)) {
31
+ // You probably want to guard against something like this,
32
+ // it should never occur unless someone's actively trying to break something.
33
+ throw new Error("You can't skip a step that isn't optional.");
34
+ }
35
+ setActiveStep(prevActiveStep => prevActiveStep + 1);
36
+ setSkipped((prevSkipped) => {
37
+ const newSkipped = new Set(prevSkipped.values());
38
+ newSkipped.add(activeStep);
39
+ return newSkipped;
40
+ });
41
+ };
42
+ const handleReset = () => {
43
+ setActiveStep(0);
44
+ };
45
+ useEffect(() => {
46
+ if (activeStep === steps.length) {
47
+ onFinish();
48
+ }
49
+ }, [activeStep, steps]);
50
+ return (_jsxs(Grid, { item: true, container: true, xs: 12, children: [_jsx(Grid, { item: true, container: true, xs: 12, children: _jsx(Box, { className: classes.fullWidthBox, children: _jsx(Stepper, { activeStep: activeStep, children: steps.map((label, index) => {
51
+ const stepProps = {};
52
+ const labelProps = {};
53
+ if (isStepOptional(index)) {
54
+ labelProps.optional = (_jsx(Typography, { variant: "caption", children: "Optional" }));
55
+ }
56
+ if (isStepSkipped(index)) {
57
+ stepProps.completed = false;
58
+ }
59
+ return (_jsx(Step, { ...stepProps, children: _jsx(StepLabel, { ...labelProps, children: label }) }, label));
60
+ }) }) }) }), _jsxs(Grid, { item: true, container: true, xs: 12, style: { marginTop: '20px' }, alignItems: "flex-start", children: [_jsx(Grid, { item: true, container: true, xs: 8, children: activeStep === steps.length ? (_jsx(_Fragment, { children: _jsx(Typography, { style: { marginTop: '20px', marginBottom: '10px' }, children: "All steps completed - you're finished" }) })) : (_jsx(_Fragment, { children: activeStep === 0 ? (_jsx(SelectStratification, { autocompleteNode: autocompleteNode, currentModalityAgnosticSelection: currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection: setCurrentModalityAgnosticSelection, currentModalitySpecificSelection: currentModalitySpecificSelection, setCurrentModalitySpecificSelection: setCurrentModalitySpecificSelection, currentStratificationSelection: currentStratificationSelection, setCurrentStratificationSelection: setCurrentStratificationSelection, getEdges: getEdges, stratifications: stratifications })) : null })) }), _jsx(Grid, { item: true, container: true, xs: 4, children: _jsx(ConfirmatoryCart, { currentModalityAgnosticSelection: currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection: setCurrentModalityAgnosticSelection, currentModalitySpecificSelection: currentModalitySpecificSelection, setCurrentModalitySpecificSelection: setCurrentModalitySpecificSelection, currentStratificationSelection: currentStratificationSelection, setCurrentStratificationSelection: setCurrentStratificationSelection, stratifications: stratifications }) }), _jsx(Box, { className: classes.fullWidthBox, children: activeStep === steps.length ? (_jsx(_Fragment, { children: _jsxs(Box, { style: { display: 'flex', flexDirection: 'row', paddingTop: '20px' }, children: [_jsx(Box, { style: { flex: '1 1 auto' } }), _jsx(Button, { onClick: handleReset, children: "Reset" })] }) })) : (_jsxs(Box, { style: { display: 'flex', flexDirection: 'row', paddingTop: '20px' }, children: [_jsx(Button, { color: "inherit", disabled: activeStep === 0, onClick: handleBack, style: { mr: 1 }, children: "Back" }), _jsx(Box, { style: { flex: '1 1 auto' } }), isStepOptional(activeStep) && (_jsx(Button, { color: "inherit", onClick: handleSkip, style: { marginRight: '10px' }, children: "Skip" })), _jsx(Button, { onClick: handleNext, children: activeStep === steps.length - 1 ? 'Finish' : 'Next' })] })) })] })] }));
61
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitessce/biomarker-select",
3
- "version": "3.5.7",
3
+ "version": "3.5.8",
4
4
  "author": "Gehlenborg Lab",
5
5
  "homepage": "http://vitessce.io",
6
6
  "repository": {
@@ -24,9 +24,9 @@
24
24
  "react-window": "^1.8.10",
25
25
  "fuse.js": "^7.0.0",
26
26
  "d3-dsv": "^1.1.1",
27
- "@vitessce/constants-internal": "3.5.7",
28
- "@vitessce/vit-s": "3.5.7",
29
- "@vitessce/types": "3.5.7"
27
+ "@vitessce/constants-internal": "3.5.8",
28
+ "@vitessce/vit-s": "3.5.8",
29
+ "@vitessce/types": "3.5.8"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@testing-library/jest-dom": "^5.16.4",
@@ -1,17 +1,20 @@
1
1
  /* eslint-disable max-len */
2
2
  /* eslint-disable no-unused-vars */
3
- import React, { useState, useCallback } from 'react';
3
+ import React, { useState, useCallback, useMemo } from 'react';
4
4
  import {
5
5
  useAsyncFunction,
6
6
  useViewConfigStoreApi,
7
7
  useSetViewConfig,
8
8
  useViewConfig,
9
9
  useCoordination,
10
+ useLoaders,
11
+ useComparisonMetadata,
12
+ useMatchingLoader,
13
+ useColumnNameMapping,
10
14
  } from '@vitessce/vit-s';
11
- import { AsyncFunctionType, ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
15
+ import { AsyncFunctionType, ViewType, COMPONENT_COORDINATION_TYPES, DataType } from '@vitessce/constants-internal';
12
16
  import { ScmdUi } from './scmd-ui.js';
13
17
 
14
-
15
18
  /**
16
19
  *
17
20
  * @param {object} props
@@ -21,10 +24,15 @@ import { ScmdUi } from './scmd-ui.js';
21
24
  export function BiomarkerSelectSubscriber(props) {
22
25
  const {
23
26
  coordinationScopes,
24
- stratificationOptions,
27
+ stratificationOptions: stratificationOptionsProp, // TODO: Remove; Get from comparisonMetadata instead
25
28
  } = props;
26
29
 
30
+ const loaders = useLoaders();
31
+
27
32
  const [{
33
+ dataset,
34
+ obsType,
35
+ sampleType,
28
36
  sampleSetSelection,
29
37
  featureSelection,
30
38
  }, {
@@ -40,6 +48,16 @@ export function BiomarkerSelectSubscriber(props) {
40
48
  const setViewConfig = useSetViewConfig(viewConfigStoreApi);
41
49
 
42
50
 
51
+ // Need obsSets and sampleSets options to obtain mapping between
52
+ // group names and column names.
53
+ const obsSetsLoader = useMatchingLoader(
54
+ loaders, dataset, DataType.OBS_SETS, { obsType },
55
+ );
56
+ const sampleSetsLoader = useMatchingLoader(
57
+ loaders, dataset, DataType.SAMPLE_SETS, { sampleType },
58
+ );
59
+ const sampleSetsColumnNameMappingReversed = useColumnNameMapping(sampleSetsLoader, true);
60
+
43
61
  // TODO: make isSelecting a coordination type plugin.
44
62
  // TODO: use store hooks from @vitessce/vit-s to update the view config based on the selections.
45
63
  const [isSelecting, setIsSelecting] = useState(true);
@@ -62,6 +80,43 @@ export function BiomarkerSelectSubscriber(props) {
62
80
  [transformFeature],
63
81
  );
64
82
 
83
+ const [{ comparisonMetadata }, cmpMetadataStatus, cmpMetadataUrls] = useComparisonMetadata(
84
+ loaders, dataset, false, {}, {}, { obsType, sampleType },
85
+ );
86
+ const stratificationOptions = useMemo(() => {
87
+ /*
88
+ return array of objects like {
89
+ stratificationId: 'aki-vs-hr',
90
+ name: 'Acute kidney injury (AKI) vs. Healthy reference',
91
+ stratificationType: 'sampleSet', // key changed from 'groupType'. value changed from 'clinical'
92
+ sampleSets: [
93
+ ['Disease Type', 'AKI'],
94
+ ['Disease Type', 'Reference'],
95
+ ],
96
+ },
97
+ */
98
+ if (comparisonMetadata?.sample_group_pairs) {
99
+ return comparisonMetadata.sample_group_pairs.map((sampleGroupPair) => {
100
+ const [sampleGroupCol, sampleGroupValues] = sampleGroupPair;
101
+ const [sampleGroupCtrl, sampleGroupCase] = sampleGroupValues;
102
+ const groupName = sampleSetsColumnNameMappingReversed?.[sampleGroupCol];
103
+ return {
104
+ stratificationId: `${sampleGroupCol}_${sampleGroupCtrl}-vs-${sampleGroupCase}`,
105
+ name: `${groupName}: ${sampleGroupCtrl} vs. ${sampleGroupCase}`,
106
+ stratificationType: 'sampleSet',
107
+ sampleSets: [
108
+ // With sampleSets coming from the comparison_metadata,
109
+ // need to use loader options from obsSets and sampleSets to get mapping
110
+ // from column name to group name.
111
+ [groupName, sampleGroupCtrl],
112
+ [groupName, sampleGroupCase],
113
+ ],
114
+ };
115
+ });
116
+ }
117
+ return null;
118
+ }, [comparisonMetadata, sampleSetsColumnNameMappingReversed]);
119
+
65
120
  return (
66
121
  <>
67
122
  {isSelecting ? (
@@ -80,20 +135,39 @@ export function BiomarkerSelectSubscriber(props) {
80
135
  getEdges={getEdges}
81
136
  stratifications={stratificationOptions}
82
137
  onFinish={() => {
83
- setViewConfig({
138
+ if (mode === 'exploratory') {
139
+ // mode is exploratory, configure accordingly.
140
+
141
+ // TODO
142
+
143
+ } else {
144
+ // mode is confirmatory, configure accordingly.
145
+
146
+ // TODO
147
+ }
148
+
149
+ const newViewConfig = {
84
150
  ...viewConfig,
85
151
  coordinationSpace: {
86
152
  ...viewConfig.coordinationSpace,
87
153
  sampleSetFilter: {
88
154
  ...viewConfig.coordinationSpace.sampleSetFilter,
89
- 'case-control': currentStratificationSelection?.sampleSets,
155
+ __comparison__: currentStratificationSelection?.sampleSets,
90
156
  },
91
157
  sampleSetSelection: {
92
158
  ...viewConfig.coordinationSpace.sampleSetSelection,
93
- 'case-control': currentStratificationSelection?.sampleSets,
159
+ __comparison__: currentStratificationSelection?.sampleSets,
160
+ },
161
+ featureSelection: {
162
+ ...viewConfig.coordinationSpace.featureSelection,
163
+ __comparison__: currentModalitySpecificSelection ? currentModalitySpecificSelection.map(d => d.label) : null,
94
164
  },
95
165
  },
96
- });
166
+ };
167
+
168
+ // TODO: can the normal coordination value setters be used instead?
169
+ // (e.g., setFeatureSelection, setSampleSetSelection)
170
+ setViewConfig(newViewConfig);
97
171
  }}
98
172
  />
99
173
  ) : null}
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import {
3
+ useCoordination,
4
+ } from '@vitessce/vit-s';
5
+ import { ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
6
+
7
+
8
+ export function ComparativeHeadingSubscriber(props) {
9
+ const {
10
+ coordinationScopes,
11
+ } = props;
12
+
13
+ const [{
14
+ sampleSetSelection,
15
+ }] = useCoordination(
16
+ COMPONENT_COORDINATION_TYPES[ViewType.COMPARATIVE_HEADING],
17
+ coordinationScopes,
18
+ );
19
+
20
+ return (
21
+ <>
22
+ {sampleSetSelection && sampleSetSelection.length === 2 ? (
23
+ <div style={{ width: '100%', display: 'flex', flexDirection: 'row' }}>
24
+ <div style={{ width: '45%' }}><h2>{sampleSetSelection?.[0]?.at(-1)}</h2></div>
25
+ <div style={{ width: '5%' }}><h2 style={{ textAlign: 'right' }}>vs.&nbsp;</h2></div>
26
+ <div style={{ width: '50%' }}><h2>{sampleSetSelection?.[1]?.at(-1)}</h2></div>
27
+ </div>
28
+ ) : null}
29
+ </>
30
+ );
31
+ }
@@ -180,6 +180,7 @@ export async function transformFeature({ queryClient }, node, targetModality) {
180
180
  queryFn: loadPathwayToGeneEdges,
181
181
  });
182
182
 
183
+ // TODO: support matching using ontology term (rather than requiring kgId)?
183
184
  const matchingEdges = pathwayGeneEdges.filter((/** @type {KgEdge} */ d) => d.source === node.kgId);
184
185
  const matchingGeneIds = matchingEdges.map((/** @type {KgEdge} */ d) => d.target);
185
186
  const matchingGenes = geneNodes.filter(d => matchingGeneIds.includes(d.kgId));
package/src/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { BiomarkerSelectSubscriber } from './BiomarkerSelectSubscriber.js';
2
+ export { ComparativeHeadingSubscriber } from './ComparativeHeadingSubscriber.js';
2
3
  export {
3
4
  autocompleteFeature,
4
5
  transformFeature,
package/src/scmd-ui.js CHANGED
@@ -1,7 +1,8 @@
1
1
  /* eslint-disable no-unused-vars */
2
2
  import React, { useState } from 'react';
3
3
  import { Button, ButtonGroup, Grid, Tooltip } from '@material-ui/core';
4
- import { ConfirmatoryStepper } from './confirmatory-stepper.js';
4
+ import { ConfirmatoryStepper } from './stepper-confirmatory.js';
5
+ import { ExploratoryStepper } from './stepper-exploratory.js';
5
6
  import { useStyles } from './styles.js';
6
7
 
7
8
 
@@ -67,7 +68,22 @@ export function ScmdUi(props) {
67
68
  onFinish={onFinish}
68
69
  />
69
70
  ) : null}
70
- {mode === 'exploratory' ? <span>TODO</span> : null}
71
+ {mode === 'exploratory' ? (
72
+ <ExploratoryStepper
73
+ autocompleteNode={autocompleteNode}
74
+
75
+ currentModalityAgnosticSelection={currentModalityAgnosticSelection}
76
+ setCurrentModalityAgnosticSelection={setCurrentModalityAgnosticSelection}
77
+ currentModalitySpecificSelection={currentModalitySpecificSelection}
78
+ setCurrentModalitySpecificSelection={setCurrentModalitySpecificSelection}
79
+ currentStratificationSelection={currentStratificationSelection}
80
+ setCurrentStratificationSelection={setCurrentStratificationSelection}
81
+
82
+ getEdges={getEdges}
83
+ stratifications={stratifications}
84
+ onFinish={onFinish}
85
+ />
86
+ ) : null}
71
87
  </div>
72
88
  ) : null}
73
89
  </Grid>
@@ -0,0 +1,175 @@
1
+ import React, { useEffect } from 'react';
2
+ import { Stepper, Step, StepLabel, Button, Grid, Box, Typography } from '@material-ui/core';
3
+ import { ConfirmatoryCart } from './cart.js';
4
+ import { SelectStratification } from './select-stratification.js';
5
+ import { useStyles } from './styles.js';
6
+
7
+ const steps = [
8
+ 'Define case-control stratification',
9
+ ];
10
+
11
+ export function ExploratoryStepper(props) {
12
+ const {
13
+ autocompleteNode,
14
+
15
+ currentModalityAgnosticSelection,
16
+ setCurrentModalityAgnosticSelection,
17
+ currentModalitySpecificSelection,
18
+ setCurrentModalitySpecificSelection,
19
+ currentStratificationSelection,
20
+ setCurrentStratificationSelection,
21
+
22
+ getEdges,
23
+ stratifications,
24
+ onFinish,
25
+ } = props;
26
+ const classes = useStyles();
27
+ const [activeStep, setActiveStep] = React.useState(0);
28
+ const [skipped, setSkipped] = React.useState(new Set());
29
+
30
+ const isStepOptional = step => step === 2;
31
+
32
+ const isStepSkipped = step => skipped.has(step);
33
+
34
+ const handleNext = () => {
35
+ let newSkipped = skipped;
36
+ if (isStepSkipped(activeStep)) {
37
+ newSkipped = new Set(newSkipped.values());
38
+ newSkipped.delete(activeStep);
39
+ }
40
+
41
+ setActiveStep(prevActiveStep => prevActiveStep + 1);
42
+ setSkipped(newSkipped);
43
+ };
44
+
45
+ const handleBack = () => {
46
+ setActiveStep(prevActiveStep => prevActiveStep - 1);
47
+ };
48
+
49
+ const handleSkip = () => {
50
+ if (!isStepOptional(activeStep)) {
51
+ // You probably want to guard against something like this,
52
+ // it should never occur unless someone's actively trying to break something.
53
+ throw new Error("You can't skip a step that isn't optional.");
54
+ }
55
+
56
+ setActiveStep(prevActiveStep => prevActiveStep + 1);
57
+ setSkipped((prevSkipped) => {
58
+ const newSkipped = new Set(prevSkipped.values());
59
+ newSkipped.add(activeStep);
60
+ return newSkipped;
61
+ });
62
+ };
63
+
64
+ const handleReset = () => {
65
+ setActiveStep(0);
66
+ };
67
+
68
+ useEffect(() => {
69
+ if (activeStep === steps.length) {
70
+ onFinish();
71
+ }
72
+ }, [activeStep, steps]);
73
+
74
+
75
+ return (
76
+ <Grid item container xs={12}>
77
+ <Grid item container xs={12}>
78
+ <Box className={classes.fullWidthBox}>
79
+ <Stepper activeStep={activeStep}>
80
+ {steps.map((label, index) => {
81
+ const stepProps = {};
82
+ const labelProps = {};
83
+ if (isStepOptional(index)) {
84
+ labelProps.optional = (
85
+ <Typography variant="caption">Optional</Typography>
86
+ );
87
+ }
88
+ if (isStepSkipped(index)) {
89
+ stepProps.completed = false;
90
+ }
91
+ return (
92
+ <Step key={label} {...stepProps}>
93
+ <StepLabel {...labelProps}>{label}</StepLabel>
94
+ </Step>
95
+ );
96
+ })}
97
+ </Stepper>
98
+ </Box>
99
+ </Grid>
100
+ <Grid item container xs={12} style={{ marginTop: '20px' }} alignItems="flex-start">
101
+ {/* Step contents to left of cart */}
102
+ <Grid item container xs={8}>
103
+ {activeStep === steps.length ? (
104
+ <>
105
+ <Typography style={{ marginTop: '20px', marginBottom: '10px' }}>
106
+ All steps completed - you&apos;re finished
107
+ </Typography>
108
+ </>
109
+ ) : (
110
+ <>
111
+ {activeStep === 0 ? (
112
+ <SelectStratification
113
+ autocompleteNode={autocompleteNode}
114
+
115
+ currentModalityAgnosticSelection={currentModalityAgnosticSelection}
116
+ setCurrentModalityAgnosticSelection={setCurrentModalityAgnosticSelection}
117
+ currentModalitySpecificSelection={currentModalitySpecificSelection}
118
+ setCurrentModalitySpecificSelection={setCurrentModalitySpecificSelection}
119
+ currentStratificationSelection={currentStratificationSelection}
120
+ setCurrentStratificationSelection={setCurrentStratificationSelection}
121
+
122
+ getEdges={getEdges}
123
+ stratifications={stratifications}
124
+ />
125
+ ) : null}
126
+ </>
127
+ )}
128
+ </Grid>
129
+ {/* Cart */}
130
+ <Grid item container xs={4}>
131
+ <ConfirmatoryCart
132
+ currentModalityAgnosticSelection={currentModalityAgnosticSelection}
133
+ setCurrentModalityAgnosticSelection={setCurrentModalityAgnosticSelection}
134
+ currentModalitySpecificSelection={currentModalitySpecificSelection}
135
+ setCurrentModalitySpecificSelection={setCurrentModalitySpecificSelection}
136
+ currentStratificationSelection={currentStratificationSelection}
137
+ setCurrentStratificationSelection={setCurrentStratificationSelection}
138
+ stratifications={stratifications}
139
+ />
140
+ </Grid>
141
+ {/* Step navigation button footer */}
142
+ <Box className={classes.fullWidthBox}>
143
+ {activeStep === steps.length ? (
144
+ <>
145
+ <Box style={{ display: 'flex', flexDirection: 'row', paddingTop: '20px' }}>
146
+ <Box style={{ flex: '1 1 auto' }} />
147
+ <Button onClick={handleReset}>Reset</Button>
148
+ </Box>
149
+ </>
150
+ ) : (
151
+ <Box style={{ display: 'flex', flexDirection: 'row', paddingTop: '20px' }}>
152
+ <Button
153
+ color="inherit"
154
+ disabled={activeStep === 0}
155
+ onClick={handleBack}
156
+ style={{ mr: 1 }}
157
+ >
158
+ Back
159
+ </Button>
160
+ <Box style={{ flex: '1 1 auto' }} />
161
+ {isStepOptional(activeStep) && (
162
+ <Button color="inherit" onClick={handleSkip} style={{ marginRight: '10px' }}>
163
+ Skip
164
+ </Button>
165
+ )}
166
+ <Button onClick={handleNext}>
167
+ {activeStep === steps.length - 1 ? 'Finish' : 'Next'}
168
+ </Button>
169
+ </Box>
170
+ )}
171
+ </Box>
172
+ </Grid>
173
+ </Grid>
174
+ );
175
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"confirmatory-stepper.d.ts","sourceRoot":"","sources":["../src/confirmatory-stepper.js"],"names":[],"mappings":"AAcA,6DAkMC"}