@vitessce/vit-s 3.4.12 → 3.5.0

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
@@ -27921,7 +27921,8 @@ const ViewType$1 = {
27921
27921
  OBS_SET_FEATURE_VALUE_DISTRIBUTION: "obsSetFeatureValueDistribution",
27922
27922
  FEATURE_VALUE_HISTOGRAM: "featureValueHistogram",
27923
27923
  DOT_PLOT: "dotPlot",
27924
- FEATURE_BAR_PLOT: "featureBarPlot"
27924
+ FEATURE_BAR_PLOT: "featureBarPlot",
27925
+ BIOMARKER_SELECT: "biomarkerSelect"
27925
27926
  };
27926
27927
  const DataType$1 = {
27927
27928
  OBS_LABELS: "obsLabels",
@@ -27960,6 +27961,9 @@ const FileType$1 = {
27960
27961
  // OME-Zarr
27961
27962
  IMAGE_OME_ZARR: "image.ome-zarr",
27962
27963
  OBS_SEGMENTATIONS_OME_ZARR: "obsSegmentations.ome-zarr",
27964
+ // OME-Zarr - Zipped
27965
+ IMAGE_OME_ZARR_ZIP: "image.ome-zarr.zip",
27966
+ OBS_SEGMENTATIONS_OME_ZARR_ZIP: "obsSegmentations.ome-zarr.zip",
27963
27967
  // AnnData
27964
27968
  OBS_FEATURE_MATRIX_ANNDATA_ZARR: "obsFeatureMatrix.anndata.zarr",
27965
27969
  OBS_FEATURE_COLUMNS_ANNDATA_ZARR: "obsFeatureColumns.anndata.zarr",
@@ -28248,10 +28252,10 @@ const AUTO_INDEPENDENT_COORDINATION_TYPES = [
28248
28252
  CoordinationType$1.EMBEDDING_OBS_OPACITY
28249
28253
  ];
28250
28254
  const note = "This file is autogenerated by .changeset/post-changelog.mjs.";
28251
- const version = "3.4.12";
28252
- const date = "2024-10-02";
28255
+ const version = "3.5.0";
28256
+ const date = "2024-11-01";
28253
28257
  const branch = "changeset-release/main";
28254
- const hash = "ce99ac9e";
28258
+ const hash = "1eb007aa";
28255
28259
  const META_VERSION = {
28256
28260
  note,
28257
28261
  version,
@@ -28631,24 +28635,29 @@ const imageOmeZarrSchema = z.object({
28631
28635
  imageOmeZarrSchema.extend({
28632
28636
  obsTypesFromChannelNames: z.boolean().optional()
28633
28637
  });
28634
- const imageSpatialdataSchema = imageOmeZarrSchema.extend({
28635
- path: z.string()
28638
+ const imageSpatialdataSchema = z.object({
28639
+ path: z.string(),
28640
+ coordinateSystem: z.string().optional().describe('The name of a coordinate transformation output used to transform the image. If not provided, the "global" coordinate system is assumed.')
28636
28641
  });
28637
28642
  const obsSegmentationsSpatialdataSchema = z.object({
28638
- // TODO: should this also extend the imageOmeZarrSchema?
28639
28643
  // TODO: should this be renamed labelsSpatialdataSchema?
28640
28644
  // TODO: support obsTypesFromChannelNames?
28641
- path: z.string()
28645
+ path: z.string(),
28646
+ tablePath: z.string().optional().describe("The path to a table which annotates the labels. If available but not specified, the spot identifiers may not be aligned with associated tabular data as expected."),
28647
+ coordinateSystem: z.string().optional().describe('The name of a coordinate transformation output used to transform the image. If not provided, the "global" coordinate system is assumed.')
28642
28648
  });
28643
28649
  z.object({
28644
- path: z.string()
28650
+ path: z.string(),
28651
+ coordinateSystem: z.string().optional().describe('The name of a coordinate transformation output used to transform the coordinates. If not provided, the "global" coordinate system is assumed.')
28645
28652
  });
28646
28653
  const obsSpotsSpatialdataSchema = z.object({
28647
28654
  path: z.string(),
28648
- tablePath: z.string().optional().describe("The path to a table which annotates the spots. If available but not specified, the spot identifiers may not be aligned with associated tabular data as expected.")
28655
+ tablePath: z.string().optional().describe("The path to a table which annotates the spots. If available but not specified, the spot identifiers may not be aligned with associated tabular data as expected."),
28656
+ coordinateSystem: z.string().optional().describe('The name of a coordinate transformation output used to transform the coordinates and radii. If not provided, the "global" coordinate system is assumed.')
28649
28657
  });
28650
28658
  const obsFeatureMatrixSpatialdataSchema = annDataObsFeatureMatrix.extend({
28651
- region: z.string().describe("The name of a region to use to filter instances (i.e., rows) in the table").optional()
28659
+ region: z.string().describe("The name of a region to use to filter instances (i.e., rows) in the table").optional(),
28660
+ coordinateSystem: z.string().optional().describe('The name of a coordinate transformation output used to transform the image. If not provided, the "global" coordinate system is assumed.')
28652
28661
  });
28653
28662
  const obsSetsSpatialdataSchema = z.object({
28654
28663
  region: z.string().describe("The name of a region to use to filter instances (i.e., rows) in the table").optional(),
@@ -28749,15 +28758,21 @@ anndataZarrSchema.extend({
28749
28758
  z.object({
28750
28759
  // TODO: should `image` be a special schema
28751
28760
  // to allow specifying fileUid (like for embeddingType)?
28761
+ // TODO: allow multiple images
28752
28762
  image: imageSpatialdataSchema,
28753
28763
  // TODO: should this be a special schema
28754
28764
  // to allow specifying fileUid (like for embeddingType)?
28765
+ // TODO: allow multiple labels
28755
28766
  labels: obsSegmentationsSpatialdataSchema,
28756
28767
  obsFeatureMatrix: obsFeatureMatrixSpatialdataSchema,
28757
28768
  obsSpots: obsSpotsSpatialdataSchema,
28758
28769
  // TODO: obsPoints
28759
28770
  // TODO: obsLocations
28760
- obsSets: obsSetsSpatialdataSchema
28771
+ obsSets: obsSetsSpatialdataSchema,
28772
+ // TODO: obsEmbedding
28773
+ // TODO: obsLabels
28774
+ // TODO: featureLabels
28775
+ coordinateSystem: z.string().optional().describe("The name of a coordinate transformation output used to transform all elements which lack a per-element coordinateSystem property.")
28761
28776
  }).partial();
28762
28777
  z.object({
28763
28778
  obsLabelsTypes: z.array(z.string()).optional(),
@@ -29268,7 +29283,7 @@ class VitessceConfigView {
29268
29283
  * @param {number} w The width of the view in the layout.
29269
29284
  * @param {number} h The height of the view in the layout.
29270
29285
  */
29271
- constructor(component, coordinationScopes, x, y, w, h) {
29286
+ constructor(component, coordinationScopes, x, y, w, h, uid) {
29272
29287
  this.view = {
29273
29288
  component,
29274
29289
  coordinationScopes,
@@ -29277,7 +29292,8 @@ class VitessceConfigView {
29277
29292
  x,
29278
29293
  y,
29279
29294
  w,
29280
- h
29295
+ h,
29296
+ uid
29281
29297
  };
29282
29298
  }
29283
29299
  /**
@@ -29531,7 +29547,7 @@ class VitessceConfig {
29531
29547
  * @returns {VitessceConfigView} A new view instance.
29532
29548
  */
29533
29549
  addView(dataset, component, options) {
29534
- const { x = 0, y = 0, w = 1, h = 1, mapping = null } = options || {};
29550
+ const { x = 0, y = 0, w = 1, h = 1, uid = void 0, mapping = null } = options || {};
29535
29551
  const datasetMatches = this.config.coordinationSpace[CoordinationType$1.DATASET] ? Object.entries(this.config.coordinationSpace[CoordinationType$1.DATASET]).filter(([scopeName, datasetScope2]) => datasetScope2.cValue === dataset.dataset.uid).map(([scopeName]) => scopeName) : [];
29536
29552
  let datasetScope;
29537
29553
  if (datasetMatches.length === 1) {
@@ -29542,7 +29558,7 @@ class VitessceConfig {
29542
29558
  const coordinationScopes = {
29543
29559
  [CoordinationType$1.DATASET]: datasetScope
29544
29560
  };
29545
- const newView = new VitessceConfigView(component, coordinationScopes, x, y, w, h);
29561
+ const newView = new VitessceConfigView(component, coordinationScopes, x, y, w, h, uid);
29546
29562
  if (mapping) {
29547
29563
  const [etScope] = this.addCoordination(CoordinationType$1.EMBEDDING_TYPE);
29548
29564
  etScope.setValue(mapping);
@@ -29560,9 +29576,20 @@ class VitessceConfig {
29560
29576
  addCoordination(...args) {
29561
29577
  const cTypes = args;
29562
29578
  const result = [];
29563
- cTypes.forEach((cType) => {
29564
- const prevScopes = this.config.coordinationSpace[cType] ? Object.keys(this.config.coordinationSpace[cType]) : [];
29565
- const scope = new VitessceConfigCoordinationScope(cType, this.getNextScope(prevScopes));
29579
+ cTypes.forEach((cTypeOrObj) => {
29580
+ let cType;
29581
+ let cScope;
29582
+ let cValue;
29583
+ if (typeof cTypeOrObj === "string") {
29584
+ cType = cTypeOrObj;
29585
+ const prevScopes = this.config.coordinationSpace[cType] ? Object.keys(this.config.coordinationSpace[cType]) : [];
29586
+ cScope = this.getNextScope(prevScopes);
29587
+ } else {
29588
+ cType = cTypeOrObj.cType;
29589
+ cScope = cTypeOrObj.cScope;
29590
+ cValue = cTypeOrObj.cValue;
29591
+ }
29592
+ const scope = new VitessceConfigCoordinationScope(cType, cScope, cValue);
29566
29593
  if (!this.config.coordinationSpace[scope.cType]) {
29567
29594
  this.config.coordinationSpace[scope.cType] = {};
29568
29595
  }
@@ -29809,7 +29836,7 @@ class VitessceConfig {
29809
29836
  }
29810
29837
  });
29811
29838
  config2.layout.forEach((c) => {
29812
- const newView = new VitessceConfigView(c.component, c.coordinationScopes, c.x, c.y, c.w, c.h);
29839
+ const newView = new VitessceConfigView(c.component, c.coordinationScopes, c.x, c.y, c.w, c.h, c.uid);
29813
29840
  vc.config.layout.push(newView);
29814
29841
  });
29815
29842
  return vc;
@@ -30762,6 +30789,9 @@ function useSetLoaders() {
30762
30789
  function useMergeCoordination() {
30763
30790
  return useViewConfigStore((state) => state.mergeCoordination);
30764
30791
  }
30792
+ function useViewConfig() {
30793
+ return useViewConfigStore((state) => state.viewConfig);
30794
+ }
30765
30795
  function useSetViewConfig(viewConfigStoreApi) {
30766
30796
  const setViewConfigRef = useRef(viewConfigStoreApi.getState().setViewConfig);
30767
30797
  const setViewConfig = setViewConfigRef.current;
@@ -36699,6 +36729,36 @@ function resolveLayout(layout2) {
36699
36729
  components
36700
36730
  };
36701
36731
  }
36732
+ const AsyncFunctionsContext = createContext$1({
36733
+ // {
36734
+ // asyncFunctions: { [functionType: string]: asyncFunction },
36735
+ // queryClient: QueryClient from react-query
36736
+ // }
36737
+ // asyncFunctions will be an object where keys are function types
36738
+ // (as strings) and values are the async functions.
36739
+ });
36740
+ function useAsyncFunction(functionType2) {
36741
+ var _a;
36742
+ const context = useContext(AsyncFunctionsContext);
36743
+ const origFunction = (_a = context == null ? void 0 : context.asyncFunctions) == null ? void 0 : _a[functionType2];
36744
+ const queryClient = context == null ? void 0 : context.queryClient;
36745
+ const partialFunction = (...args) => origFunction({ queryClient }, ...args);
36746
+ return partialFunction;
36747
+ }
36748
+ const PageModeViewContext = createContext$1({
36749
+ // Keys are view UIDs and values are
36750
+ // the corresponding React components.
36751
+ });
36752
+ function ensureComponent(component) {
36753
+ if (!component) {
36754
+ return () => null;
36755
+ }
36756
+ return component;
36757
+ }
36758
+ function usePageModeView(uid) {
36759
+ const context = useContext(PageModeViewContext);
36760
+ return ensureComponent(context == null ? void 0 : context[uid]);
36761
+ }
36702
36762
  const ResponsiveGridLayout = reactGridLayoutWithLodashExports.WidthProvider(reactGridLayoutWithLodashExports.Responsive);
36703
36763
  class ResponsiveHeightGridLayout extends ResponsiveGridLayout {
36704
36764
  componentDidUpdate(prevProps) {
@@ -36798,30 +36858,23 @@ function VitessceGridLayout(props) {
36798
36858
  const saveCurrentLayouts = useCallback(() => {
36799
36859
  setLastValidGridLayouts(gridLayouts);
36800
36860
  }, [gridLayouts]);
36801
- const layoutChildren = useMemo(() => {
36861
+ const [layoutChildren, pageModeViews] = useMemo(() => {
36802
36862
  const gridComponentsValues = Object.values(gridComponents);
36803
- if (pageMode) {
36804
- return children2.map((child) => {
36805
- const childId = child.props.id;
36806
- const v = gridComponentsValues.find((el2) => el2.uid === childId);
36807
- if (!v) {
36808
- return child;
36809
- }
36810
- const Component = getComponent(v.component);
36811
- return React__default.cloneElement(child, { key: v.uid }, /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { display: "flex", flexDirection: "column", height: "100%" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
36812
- Component,
36813
- {
36814
- ...v.props,
36815
- uuid: v.uid,
36816
- coordinationScopes: v.coordinationScopes,
36817
- coordinationScopesBy: v.coordinationScopesBy,
36818
- theme,
36819
- removeGridComponent: null
36820
- }
36821
- ) }));
36822
- });
36823
- }
36824
- return gridComponentsValues.map((v) => {
36863
+ const contextValue = Object.fromEntries(gridComponentsValues.map((v) => {
36864
+ const Component = getComponent(v.component);
36865
+ return [v.uid, () => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { display: "flex", flexDirection: "column", height: "100%" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
36866
+ Component,
36867
+ {
36868
+ ...v.props,
36869
+ uuid: v.uid,
36870
+ coordinationScopes: v.coordinationScopes,
36871
+ coordinationScopesBy: v.coordinationScopesBy,
36872
+ theme,
36873
+ removeGridComponent: null
36874
+ }
36875
+ ) }, v.uid)];
36876
+ }));
36877
+ return [gridComponentsValues.map((v) => {
36825
36878
  const Component = getComponent(v.component);
36826
36879
  const removeGridComponent = () => {
36827
36880
  onRemoveComponent(v.uid);
@@ -36837,9 +36890,9 @@ function VitessceGridLayout(props) {
36837
36890
  removeGridComponent
36838
36891
  }
36839
36892
  ) }, v.uid);
36840
- });
36893
+ }), contextValue];
36841
36894
  }, [gridComponents, getComponent, onRemoveComponent, theme]);
36842
- return pageMode ? layoutChildren : gridLayouts && gridComponents && gridBreakpoints && gridCols && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
36895
+ return pageMode ? /* @__PURE__ */ jsxRuntimeExports.jsx(PageModeViewContext.Provider, { value: pageModeViews, children: children2 }) : gridLayouts && gridComponents && gridBreakpoints && gridCols && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
36843
36896
  style,
36844
36897
  /* @__PURE__ */ jsxRuntimeExports.jsx(
36845
36898
  ResponsiveHeightGridLayout,
@@ -37222,18 +37275,22 @@ function useUint8ObsFeatureMatrix({ obsFeatureMatrix }) {
37222
37275
  }
37223
37276
  function useUint8FeatureSelection(expressionData) {
37224
37277
  return useMemo(() => {
37225
- if (expressionData && expressionData[0]) {
37226
- const extents = expressionData.map((arr) => extent(arr));
37227
- const normData = expressionData.map((arr, i) => {
37228
- const [min, max] = extents[i];
37229
- const ratio = 255 / (max - min);
37230
- return new Uint8Array(
37231
- arr.map((j) => Math.floor((j - min) * ratio))
37232
- );
37233
- });
37234
- return [normData, extents];
37278
+ if ((expressionData == null ? void 0 : expressionData[0]) == null) {
37279
+ return { normData: null, extents: null, missing: null };
37235
37280
  }
37236
- return [null, null];
37281
+ const extents = expressionData.map((arr) => extent(arr));
37282
+ const normData = expressionData.map((arr, i) => {
37283
+ const [min, max] = extents[i];
37284
+ const ratio = 255 / (max - min);
37285
+ return new Uint8Array(
37286
+ arr.map((j) => Math.floor((j - min) * ratio))
37287
+ );
37288
+ });
37289
+ const missing = expressionData.map((arr) => {
37290
+ const numMissing = arr.reduce((prev, curr) => Number.isNaN(curr) ? prev + 1 : prev, 0);
37291
+ return numMissing / arr.length;
37292
+ });
37293
+ return { normData, extents, missing };
37237
37294
  }, [expressionData]);
37238
37295
  }
37239
37296
  function useExpressionValueGetter({ instanceObsIndex, matrixObsIndex, expressionData }) {
@@ -37993,14 +38050,6 @@ function createGenerateClassName(customPrefix) {
37993
38050
  return `${seedPrefix}${ruleKey}`;
37994
38051
  };
37995
38052
  }
37996
- const AsyncFunctionsContext = createContext$1({
37997
- // This will be an object where keys are function types
37998
- // (as strings) and values are the async functions.
37999
- });
38000
- function useAsyncFunction(functionType2) {
38001
- const context = useContext(AsyncFunctionsContext);
38002
- return context == null ? void 0 : context[functionType2];
38003
- }
38004
38053
  function VitS(props) {
38005
38054
  const {
38006
38055
  config: config2,
@@ -38035,14 +38084,6 @@ function VitS(props) {
38035
38084
  () => coordinationTypesProp || [],
38036
38085
  [coordinationTypesProp]
38037
38086
  );
38038
- const asyncFunctions = useMemo(
38039
- () => Object.fromEntries(
38040
- // Convert the array of PluginAsyncFunction instances to a mapping
38041
- // from function type strings to async functions.
38042
- (asyncFunctionsProp == null ? void 0 : asyncFunctionsProp.map((p) => [p.functionType, p.asyncFunction])) || []
38043
- ),
38044
- [asyncFunctionsProp]
38045
- );
38046
38087
  const generateClassName2 = useMemo(() => createGenerateClassName(uid), [uid]);
38047
38088
  const configVersion = config2 == null ? void 0 : config2.version;
38048
38089
  const configKey = useMemo(() => {
@@ -38112,7 +38153,20 @@ function VitS(props) {
38112
38153
  retry: 2
38113
38154
  }
38114
38155
  }
38156
+ // TODO: should the queryClient be shared? Or have an option to be shared
38157
+ // (e.g., based on remountOnUidChange)?
38115
38158
  }), [configKey]);
38159
+ const asyncFunctions = useMemo(
38160
+ () => ({
38161
+ queryClient,
38162
+ asyncFunctions: Object.fromEntries(
38163
+ // Convert the array of PluginAsyncFunction instances to a mapping
38164
+ // from function type strings to async functions.
38165
+ (asyncFunctionsProp == null ? void 0 : asyncFunctionsProp.map((p) => [p.functionType, p.asyncFunction])) || []
38166
+ )
38167
+ }),
38168
+ [asyncFunctionsProp, queryClient]
38169
+ );
38116
38170
  useEffect(() => {
38117
38171
  if (success && !isEqual$1(configOrWarning, config2) && onConfigChange) {
38118
38172
  onConfigChange(configOrWarning);
@@ -40221,6 +40275,7 @@ export {
40221
40275
  useObsSegmentationsData,
40222
40276
  useObsSetsData,
40223
40277
  useObsSpotsData,
40278
+ usePageModeView,
40224
40279
  useStyles as usePlotOptionsStyles,
40225
40280
  usePointMultiObsLabels,
40226
40281
  useReady,
@@ -40233,6 +40288,7 @@ export {
40233
40288
  useSegmentationMultiObsSets,
40234
40289
  useSetComponentHover,
40235
40290
  useSetComponentViewInfo,
40291
+ useSetViewConfig,
40236
40292
  useSetWarning,
40237
40293
  useSpotMultiFeatureLabels,
40238
40294
  useSpotMultiFeatureSelection,
@@ -40241,6 +40297,7 @@ export {
40241
40297
  useUint8FeatureSelection,
40242
40298
  useUint8ObsFeatureMatrix,
40243
40299
  useUrls,
40300
+ useViewConfig,
40244
40301
  useViewConfigStore,
40245
40302
  useViewConfigStoreApi,
40246
40303
  useVitessceContainer,
@@ -1 +1 @@
1
- {"version":3,"file":"VitS.d.ts","sourceRoot":"","sources":["../src/VitS.js"],"names":[],"mappings":"AA+BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,4BAhCG;IAAsB,MAAM,EAApB,MAAM;IAGkB,SAAS,EAAjC,SAAS,GAAC,MAAM;IACF,MAAM,EAApB,MAAM;IACQ,KAAK,EAAnB,MAAM;IAEU,MAAM;IACN,cAAc;IAEd,cAAc;IAEf,cAAc,EAA7B,OAAO;IAIQ,sBAAsB,EAArC,OAAO;IAGY,GAAG,EAAtB,IAAI,GAAC,MAAM;IAGI,kBAAkB,EAAjC,OAAO;IAEM,SAAS,EAAtB,KAAK;IACQ,SAAS,EAAtB,KAAK;IACQ,cAAc,EAA3B,KAAK;IACQ,iBAAiB,EAA9B,KAAK;IACc,OAAO,EAA1B,IAAI,GAAC,MAAM;IACI,QAAQ,EAAvB,OAAO;CAEjB,eA+MA"}
1
+ {"version":3,"file":"VitS.d.ts","sourceRoot":"","sources":["../src/VitS.js"],"names":[],"mappings":"AA+BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,4BAhCG;IAAsB,MAAM,EAApB,MAAM;IAGkB,SAAS,EAAjC,SAAS,GAAC,MAAM;IACF,MAAM,EAApB,MAAM;IACQ,KAAK,EAAnB,MAAM;IAEU,MAAM;IACN,cAAc;IAEd,cAAc;IAEf,cAAc,EAA7B,OAAO;IAIQ,sBAAsB,EAArC,OAAO;IAGY,GAAG,EAAtB,IAAI,GAAC,MAAM;IAGI,kBAAkB,EAAjC,OAAO;IAEM,SAAS,EAAtB,KAAK;IACQ,SAAS,EAAtB,KAAK;IACQ,cAAc,EAA3B,KAAK;IACQ,iBAAiB,EAA9B,KAAK;IACc,OAAO,EAA1B,IAAI,GAAC,MAAM;IACI,QAAQ,EAAvB,OAAO;CAEjB,eAqNA"}
package/dist-tsc/VitS.js CHANGED
@@ -54,11 +54,6 @@ export function VitS(props) {
54
54
  const fileTypes = useMemo(() => (fileTypesProp || []), [fileTypesProp]);
55
55
  const jointFileTypes = useMemo(() => (jointFileTypesProp || []), [jointFileTypesProp]);
56
56
  const coordinationTypes = useMemo(() => (coordinationTypesProp || []), [coordinationTypesProp]);
57
- const asyncFunctions = useMemo(() => Object.fromEntries(
58
- // Convert the array of PluginAsyncFunction instances to a mapping
59
- // from function type strings to async functions.
60
- asyncFunctionsProp?.map(p => ([p.functionType, p.asyncFunction]))
61
- || []), [asyncFunctionsProp]);
62
57
  const generateClassName = useMemo(() => createGenerateClassName(uid), [uid]);
63
58
  const configVersion = config?.version;
64
59
  // If config.uid exists, then use it for hook dependencies to detect changes
@@ -132,7 +127,17 @@ export function VitS(props) {
132
127
  retry: 2,
133
128
  },
134
129
  },
130
+ // TODO: should the queryClient be shared? Or have an option to be shared
131
+ // (e.g., based on remountOnUidChange)?
135
132
  }), [configKey]);
133
+ const asyncFunctions = useMemo(() => ({
134
+ queryClient,
135
+ asyncFunctions: Object.fromEntries(
136
+ // Convert the array of PluginAsyncFunction instances to a mapping
137
+ // from function type strings to async functions.
138
+ asyncFunctionsProp?.map(p => ([p.functionType, p.asyncFunction]))
139
+ || []),
140
+ }), [asyncFunctionsProp, queryClient]);
136
141
  // Emit the upgraded/initialized view config
137
142
  // to onConfigChange if necessary.
138
143
  useEffect(() => {
@@ -1,3 +1,5 @@
1
- export function useAsyncFunction(functionType: any): any;
1
+ export function useAsyncFunction(functionType: any): (...args: any[]) => any;
2
+ export function usePageModeView(uid: any): any;
2
3
  export const AsyncFunctionsContext: any;
4
+ export const PageModeViewContext: any;
3
5
  //# sourceMappingURL=contexts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"contexts.d.ts","sourceRoot":"","sources":["../src/contexts.js"],"names":[],"mappings":"AAQA,yDAGC;AARD,wCAGG"}
1
+ {"version":3,"file":"contexts.d.ts","sourceRoot":"","sources":["../src/contexts.js"],"names":[],"mappings":"AAYA,6EAQC;AAcD,+CAKC;AApCD,wCAOG;AAYH,sCAGG"}
@@ -1,10 +1,35 @@
1
1
  /* eslint-disable react-refresh/only-export-components */
2
2
  import { createContext, useContext } from 'react';
3
3
  export const AsyncFunctionsContext = createContext({
4
- // This will be an object where keys are function types
4
+ // {
5
+ // asyncFunctions: { [functionType: string]: asyncFunction },
6
+ // queryClient: QueryClient from react-query
7
+ // }
8
+ // asyncFunctions will be an object where keys are function types
5
9
  // (as strings) and values are the async functions.
6
10
  });
7
11
  export function useAsyncFunction(functionType) {
8
12
  const context = useContext(AsyncFunctionsContext);
9
- return context?.[functionType];
13
+ const origFunction = context?.asyncFunctions?.[functionType];
14
+ const queryClient = context?.queryClient;
15
+ // We use partial function application here, so that the caller
16
+ // does not need to worry about passing in the queryClient.
17
+ const partialFunction = (...args) => origFunction({ queryClient }, ...args);
18
+ return partialFunction;
19
+ }
20
+ export const PageModeViewContext = createContext({
21
+ // Keys are view UIDs and values are
22
+ // the corresponding React components.
23
+ });
24
+ function ensureComponent(component) {
25
+ if (!component) {
26
+ return () => null;
27
+ }
28
+ return component;
29
+ }
30
+ export function usePageModeView(uid) {
31
+ const context = useContext(PageModeViewContext);
32
+ // TODO: Is there any case where we would want to
33
+ // throw an error instead?
34
+ return ensureComponent(context?.[uid]);
10
35
  }
@@ -57,15 +57,24 @@ export function useUint8ObsFeatureMatrix({ obsFeatureMatrix }: {
57
57
  /**
58
58
  * Normalize a feature selection (data for selected
59
59
  * columns of an obsFeatureMatrix) to a Uint8Array.
60
- * @param {array|null} expressionData The expressionData
60
+ * @param {Float32Array[] | null} expressionData The expressionData
61
61
  * returned by the useFeatureSelection hook,
62
62
  * where each element corresponds to an
63
63
  * array of values for a selected feature.
64
- * @returns {array} A tuple [normData, extents] where
65
- * normData is an array of Uint8Arrays (or null), and extents is
66
- * an array of [min, max] values for each feature (or null).
64
+ * @returns {{
65
+ * normData: Uint8Array[] | null,
66
+ * extents: [number, number][] | null,
67
+ * missing: number[]
68
+ * }} An object tuple {normData, extents, missing} where
69
+ * normData is an array of Uint8Arrays (or null),
70
+ * extents is an array of [min, max] values for each feature (or null), and
71
+ * missing is an array of numbers (between 0 and 1) for each feature (or null).
67
72
  */
68
- export function useUint8FeatureSelection(expressionData: array | null): array;
73
+ export function useUint8FeatureSelection(expressionData: Float32Array[] | null): {
74
+ normData: Uint8Array[] | null;
75
+ extents: [number, number][] | null;
76
+ missing: number[];
77
+ };
69
78
  export function useExpressionValueGetter({ instanceObsIndex, matrixObsIndex, expressionData }: {
70
79
  instanceObsIndex: any;
71
80
  matrixObsIndex: any;
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.js"],"names":[],"mappings":"AAkBA,oDAOC;AAED;;;;GAIG;AACH,uCAHa,KAAK,CAmBjB;AAED;;;;GAIG;AACH,mCAHa,KAAK,CAoCjB;AAED;;;;;GAKG;AACH,qCAJa,KAAK,CAqCjB;AAED;;;;;;GAMG;AACH,uCAHW,MAAM,EAAE,GACN,OAAO,CAOnB;AAED;;;;GAIG;AACH,8BAHW,CAAC,IAAI,GAAC,MAAM,EAAE,CAAC,EAAE,GACf,KAAK,CAYjB;AAED;;;;;;GAMG;AACH,qDAJW,GAAG,GACD,KAAK,CA0BjB;AAED;;;;;;;;;;GAUG;AACH,+DARG;IAAuB,gBAAgB,EAA/B,MAAM;CAEd,GAAU,KAAK,CAmBjB;AAED;;;;;;;;;;GAUG;AACH,yDARW,KAAK,GAAC,IAAI,GAIR,KAAK,CAmBjB;AAED;;;;QA2BC;AAED,iEAOC;AAED,kHA4BC"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.js"],"names":[],"mappings":"AAkBA,oDAOC;AAED;;;;GAIG;AACH,uCAHa,KAAK,CAmBjB;AAED;;;;GAIG;AACH,mCAHa,KAAK,CAoCjB;AAED;;;;;GAKG;AACH,qCAJa,KAAK,CAqCjB;AAED;;;;;;GAMG;AACH,uCAHW,MAAM,EAAE,GACN,OAAO,CAOnB;AAED;;;;GAIG;AACH,8BAHW,CAAC,IAAI,GAAC,MAAM,EAAE,CAAC,EAAE,GACf,KAAK,CAYjB;AAED;;;;;;GAMG;AACH,qDAJW,GAAG,GACD,KAAK,CA0BjB;AAED;;;;;;;;;;GAUG;AACH,+DARG;IAAuB,gBAAgB,EAA/B,MAAM;CAEd,GAAU,KAAK,CAmBjB;AAED;;;;;;;;;;;;;;;GAeG;AACH,yDAbW,YAAY,EAAE,GAAG,IAAI,GAInB;IACZ,QAAW,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IACjC,OAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC;IACtC,OAAU,EAAE,MAAM,EAAE,CAAA;CACjB,CAwBH;AAED;;;;QA2BC;AAED,iEAOC;AAED,kHA4BC"}
package/dist-tsc/hooks.js CHANGED
@@ -185,26 +185,35 @@ export function useUint8ObsFeatureMatrix({ obsFeatureMatrix }) {
185
185
  /**
186
186
  * Normalize a feature selection (data for selected
187
187
  * columns of an obsFeatureMatrix) to a Uint8Array.
188
- * @param {array|null} expressionData The expressionData
188
+ * @param {Float32Array[] | null} expressionData The expressionData
189
189
  * returned by the useFeatureSelection hook,
190
190
  * where each element corresponds to an
191
191
  * array of values for a selected feature.
192
- * @returns {array} A tuple [normData, extents] where
193
- * normData is an array of Uint8Arrays (or null), and extents is
194
- * an array of [min, max] values for each feature (or null).
192
+ * @returns {{
193
+ * normData: Uint8Array[] | null,
194
+ * extents: [number, number][] | null,
195
+ * missing: number[]
196
+ * }} An object tuple {normData, extents, missing} where
197
+ * normData is an array of Uint8Arrays (or null),
198
+ * extents is an array of [min, max] values for each feature (or null), and
199
+ * missing is an array of numbers (between 0 and 1) for each feature (or null).
195
200
  */
196
201
  export function useUint8FeatureSelection(expressionData) {
197
202
  return useMemo(() => {
198
- if (expressionData && expressionData[0]) {
199
- const extents = expressionData.map(arr => extent(arr));
200
- const normData = expressionData.map((arr, i) => {
201
- const [min, max] = extents[i];
202
- const ratio = 255 / (max - min);
203
- return new Uint8Array(arr.map(j => Math.floor((j - min) * ratio)));
204
- });
205
- return [normData, extents];
203
+ if (expressionData?.[0] == null) {
204
+ return { normData: null, extents: null, missing: null };
206
205
  }
207
- return [null, null];
206
+ const extents = expressionData.map(arr => extent(arr));
207
+ const normData = expressionData.map((arr, i) => {
208
+ const [min, max] = extents[i];
209
+ const ratio = 255 / (max - min);
210
+ return new Uint8Array(arr.map(j => Math.floor((j - min) * ratio)));
211
+ });
212
+ const missing = expressionData.map((arr) => {
213
+ const numMissing = arr.reduce((prev, curr) => (Number.isNaN(curr) ? prev + 1 : prev), 0);
214
+ return numMissing / arr.length;
215
+ });
216
+ return { normData, extents, missing };
208
217
  }, [expressionData]);
209
218
  }
210
219
  export function useExpressionValueGetter({ instanceObsIndex, matrixObsIndex, expressionData }) {
@@ -3,12 +3,12 @@ export { TitleInfo } from "./TitleInfo.js";
3
3
  export { PopperMenu } from "./shared-mui/components.js";
4
4
  export { useHasLoader } from "./data-hook-utils.js";
5
5
  export { logConfig } from "./view-config-utils.js";
6
- export { useAsyncFunction } from "./contexts.js";
7
6
  export { useReady, useUrls, useVitessceContainer, useDeckCanvasSize, useUint8ObsFeatureMatrix, useUint8FeatureSelection, useExpressionValueGetter, useGetObsMembership, useGetObsInfo, useClosestVitessceContainerSize, useWindowDimensions, useGridItemSize } from "./hooks.js";
8
- export { useCoordinationScopes, useCoordinationScopesBy, useInitialCoordination, useCoordination, useComplexCoordination, useComplexCoordinationSecondary, useMultiCoordinationScopes, useMultiCoordinationScopesNonNull, useMultiCoordinationScopesSecondary, useMultiCoordinationScopesSecondaryNonNull, useMultiCoordinationValues, useMultiDatasetCoordination, useDatasetUids, useLoaders, useMergeCoordination, useMatchingLoader, useViewConfigStore, useViewConfigStoreApi, useComponentHover, useSetComponentHover, useComponentViewInfo, useSetComponentViewInfo, useWarning, useSetWarning, useAuxiliaryCoordination, useComponentLayout, useRemoveImageChannelInMetaCoordinationScopes, useAddImageChannelInMetaCoordinationScopes } from "./state/hooks.js";
7
+ export { useCoordinationScopes, useCoordinationScopesBy, useInitialCoordination, useCoordination, useComplexCoordination, useComplexCoordinationSecondary, useMultiCoordinationScopes, useMultiCoordinationScopesNonNull, useMultiCoordinationScopesSecondary, useMultiCoordinationScopesSecondaryNonNull, useMultiCoordinationValues, useMultiDatasetCoordination, useDatasetUids, useLoaders, useMergeCoordination, useMatchingLoader, useViewConfigStore, useViewConfigStoreApi, useViewConfig, useSetViewConfig, useComponentHover, useSetComponentHover, useComponentViewInfo, useSetComponentViewInfo, useWarning, useSetWarning, useAuxiliaryCoordination, useComponentLayout, useRemoveImageChannelInMetaCoordinationScopes, useAddImageChannelInMetaCoordinationScopes } from "./state/hooks.js";
9
8
  export { useDescription, useImageData, useObsSetsData, useObsEmbeddingData, useFeatureSelection, useObsFeatureMatrixIndices, useSampleSetsData, useSampleEdgesData, useMultiObsLabels, useMultiObsSpots, useMultiObsPoints, useSpotMultiObsSets, useSpotMultiFeatureLabels, useMultiObsSegmentations, useMultiImages, useObsSpotsData, useObsPointsData, useObsLocationsData, useObsSegmentationsData, useNeighborhoodsData, useObsLabelsData, useObsFeatureMatrixData, useFeatureLabelsData, useGenomicProfilesData } from "./data-hooks.js";
10
9
  export { usePointMultiObsLabels, useSpotMultiFeatureSelection, useSpotMultiObsFeatureMatrixIndices, useSegmentationMultiFeatureSelection, useSegmentationMultiObsFeatureMatrixIndices, useSegmentationMultiObsLocations, useSegmentationMultiObsSets } from "./data-hooks-multilevel.js";
11
10
  export { AbstractLoader, AbstractTwoStepLoader, LoaderResult } from "./data/index.js";
12
11
  export { AbstractLoaderError, DatasetNotFoundError, LoaderNotFoundError, LoaderValidationError, DataSourceFetchError } from "./errors/index.js";
13
12
  export { CellColorEncodingOption, OptionsContainer, OptionSelect, usePlotOptionsStyles } from "./shared-plot-options/index.js";
13
+ export { useAsyncFunction, usePageModeView } from "./contexts.js";
14
14
  //# sourceMappingURL=index.d.ts.map
package/dist-tsc/index.js CHANGED
@@ -3,7 +3,7 @@ export { TitleInfo } from './TitleInfo.js';
3
3
  export { PopperMenu } from './shared-mui/components.js';
4
4
  // For plugin view types:
5
5
  export { useReady, useUrls, useVitessceContainer, useDeckCanvasSize, useUint8ObsFeatureMatrix, useUint8FeatureSelection, useExpressionValueGetter, useGetObsMembership, useGetObsInfo, useClosestVitessceContainerSize, useWindowDimensions, useGridItemSize, } from './hooks.js';
6
- export { useCoordinationScopes, useCoordinationScopesBy, useInitialCoordination, useCoordination, useComplexCoordination, useComplexCoordinationSecondary, useMultiCoordinationScopes, useMultiCoordinationScopesNonNull, useMultiCoordinationScopesSecondary, useMultiCoordinationScopesSecondaryNonNull, useMultiCoordinationValues, useMultiDatasetCoordination, useDatasetUids, useLoaders, useMergeCoordination, useMatchingLoader, useViewConfigStore, useViewConfigStoreApi, useComponentHover, useSetComponentHover, useComponentViewInfo, useSetComponentViewInfo, useWarning, useSetWarning, useAuxiliaryCoordination, useComponentLayout, useRemoveImageChannelInMetaCoordinationScopes, useAddImageChannelInMetaCoordinationScopes, } from './state/hooks.js';
6
+ export { useCoordinationScopes, useCoordinationScopesBy, useInitialCoordination, useCoordination, useComplexCoordination, useComplexCoordinationSecondary, useMultiCoordinationScopes, useMultiCoordinationScopesNonNull, useMultiCoordinationScopesSecondary, useMultiCoordinationScopesSecondaryNonNull, useMultiCoordinationValues, useMultiDatasetCoordination, useDatasetUids, useLoaders, useMergeCoordination, useMatchingLoader, useViewConfigStore, useViewConfigStoreApi, useViewConfig, useSetViewConfig, useComponentHover, useSetComponentHover, useComponentViewInfo, useSetComponentViewInfo, useWarning, useSetWarning, useAuxiliaryCoordination, useComponentLayout, useRemoveImageChannelInMetaCoordinationScopes, useAddImageChannelInMetaCoordinationScopes, } from './state/hooks.js';
7
7
  export { useDescription, useImageData, useObsSetsData, useObsEmbeddingData, useFeatureSelection, useObsFeatureMatrixIndices, useSampleSetsData, useSampleEdgesData, useMultiObsLabels, useMultiObsSpots, useMultiObsPoints, useSpotMultiObsSets, useSpotMultiFeatureLabels, useMultiObsSegmentations, useMultiImages, useObsSpotsData, useObsPointsData, useObsLocationsData, useObsSegmentationsData, useNeighborhoodsData, useObsLabelsData, useObsFeatureMatrixData, useFeatureLabelsData, useGenomicProfilesData, } from './data-hooks.js';
8
8
  export { usePointMultiObsLabels, useSpotMultiFeatureSelection, useSpotMultiObsFeatureMatrixIndices, useSegmentationMultiFeatureSelection, useSegmentationMultiObsFeatureMatrixIndices, useSegmentationMultiObsLocations, useSegmentationMultiObsSets, } from './data-hooks-multilevel.js';
9
9
  export { useHasLoader, } from './data-hook-utils.js';
@@ -11,4 +11,4 @@ export { AbstractLoader, AbstractTwoStepLoader, LoaderResult, } from './data/ind
11
11
  export { AbstractLoaderError, DatasetNotFoundError, LoaderNotFoundError, LoaderValidationError, DataSourceFetchError, } from './errors/index.js';
12
12
  export { CellColorEncodingOption, OptionsContainer, OptionSelect, usePlotOptionsStyles, } from './shared-plot-options/index.js';
13
13
  export { logConfig } from './view-config-utils.js';
14
- export { useAsyncFunction } from './contexts.js';
14
+ export { useAsyncFunction, usePageModeView } from './contexts.js';
@@ -222,6 +222,13 @@ export function useSetLoaders(): Function;
222
222
  * in the `useViewConfigStore` store.
223
223
  */
224
224
  export function useMergeCoordination(): Function;
225
+ /**
226
+ * Obtain the current view config object from
227
+ * the global app state.
228
+ * @returns {object} The view config object
229
+ * in the `useViewConfigStore` store.
230
+ */
231
+ export function useViewConfig(): object;
225
232
  /**
226
233
  * Obtain the view config setter function from
227
234
  * the global app state.
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/state/hooks.js"],"names":[],"mappings":"AAmCA;;;;;;GAMG;AACH,8CAJW,GAAC,mBAEC,MAAM,GAAC,SAAS,CA4B5B;AAED;;;;;;;GAOG;AACH,gDALW,GAAC,wBACD,GAAC,qBAEC,MAAM,GAAC,SAAS,CA0C5B;AAED;;;;;GAKG;AACH,6CAJW,MAAM,sBACN,GAAC,GACC,MAAM,GAAC,SAAS,CAI5B;AAED;;;;;;GAMG;AACH,+CALW,MAAM,mDACN,GAAC,wBACD,GAAC,GACC,MAAM,GAAC,SAAS,CAc5B;AA4RD;;;;;;;;;GASG;AACH,mDALW,MAAM,EAAE,sBACR,MAAM,GAEJ,MAAM,CAclB;AAED;;;;;;;;;;;;;;;GAeG;AACH,4CATW,MAAM,EAAE,sBACR,MAAM,GAEJ,KAAK,CAmCjB;AAED,yFAKC;AAED,gGAqBC;AAED,0IA4BC;AAED,iJAkEC;AAED,yFAmBC;AAED;;;;GAIG;AACH,mDAHW,MAAM,GACJ,MAAM,CAIlB;AAED;;;;;;;;;;;;;GAaG;AACH,mDAXW,MAAM,EAAE,sBACR,MAAM,wBACN,MAAM,UAEN,MAAM,GAEJ,KAAK,CA2EjB;AAED;;;;;GAKG;AACH,0DAHW,MAAM,GACJ,MAAM,CAiBlB;AAED;;;;;;GAMG;AACH,4DAJW,MAAM,wBACN,MAAM,GACJ,MAAM,CAgBlB;AAED;;;;;;;GAOG;AACH,4DANW,MAAM,EAAE,iDACR,MAAM,eACN,MAAM,iBACN,MAAM,SAuEhB;AAGD;;;;;;;;;;;GAWG;AACH,wDATW,MAAM,EAAE,sBACR,MAAM,wBACN,MAAM,GAEJ,KAAK,CAUjB;AA6BD;;;;;;;;;;;;;;;GAeG;AACH,qDATW,MAAM,EAAE,sBACR,MAAM,GAEJ,KAAK,CAgCjB;AAED;;;;;GAKG;AACH,8BAHa,MAAM,CAKlB;AAGD;;;;;;;;;;;;GAYG;AACH,2CARW,MAAM,WACN,MAAM,YACN,MAAM,0BACN,MAAM,OAoBhB;AAED;;;;;;;;;;;;GAYG;AACH,2CARW,MAAM,WACN,MAAM,YACN,MAAM,0BACN,MAAM,OAShB;AAED;;;;;GAKG;AACH,6BAHa,MAAM,EAAE,CAKpB;AAED;;;;;GAKG;AACH,+CAEC;AAED;;;;;GAKG;AACH,0EAEC;AAED;;;;;GAKG;AACH,uEAEC;AAED;;;;;GAKG;AACH,4CAEC;AAED;;;;;GAKG;AACH,0CAEC;AAED;;;;;GAKG;AACH,iDAEC;AAED;;;;;GAKG;AACH,oEAIC;AAED;;;;;GAKG;AACH,qCAHa,MAAM,CAKlB;AAED;;;;;GAKG;AACH,iDAEC;AAED;;;;;GAKG;AACH,8BAHa,MAAM,CAKlB;AAED;;;;;GAKG;AACH,0CAEC;AAED;;;;;GAKG;AACH,iDAHa,MAAM,CAKlB;AAED;;;;;GAKG;AACH,6DAOC;AAED;;;;GAIG;AACH,iCAFa,MAAM,CAIlB;AAED;;;;;GAKG;AACH,8CAEC;AA5oCD;;;;aAPG,OAAO,kCAAkC,OAAO,sEAOO;AAC1D,mFAA0D;AAC1D;;;;;EAAgE;AAOhE;;;;mIAAwD;AACxD,kFAAwD;AAiIjD,yFA+KJ;AAOI,0FAFM,MAAM,CAOlB;AAcM,iDAUJ"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/state/hooks.js"],"names":[],"mappings":"AAmCA;;;;;;GAMG;AACH,8CAJW,GAAC,mBAEC,MAAM,GAAC,SAAS,CA4B5B;AAED;;;;;;;GAOG;AACH,gDALW,GAAC,wBACD,GAAC,qBAEC,MAAM,GAAC,SAAS,CA0C5B;AAED;;;;;GAKG;AACH,6CAJW,MAAM,sBACN,GAAC,GACC,MAAM,GAAC,SAAS,CAI5B;AAED;;;;;;GAMG;AACH,+CALW,MAAM,mDACN,GAAC,wBACD,GAAC,GACC,MAAM,GAAC,SAAS,CAc5B;AA4RD;;;;;;;;;GASG;AACH,mDALW,MAAM,EAAE,sBACR,MAAM,GAEJ,MAAM,CAclB;AAED;;;;;;;;;;;;;;;GAeG;AACH,4CATW,MAAM,EAAE,sBACR,MAAM,GAEJ,KAAK,CAmCjB;AAED,yFAKC;AAED,gGAqBC;AAED,0IA4BC;AAED,iJAkEC;AAED,yFAmBC;AAED;;;;GAIG;AACH,mDAHW,MAAM,GACJ,MAAM,CAIlB;AAED;;;;;;;;;;;;;GAaG;AACH,mDAXW,MAAM,EAAE,sBACR,MAAM,wBACN,MAAM,UAEN,MAAM,GAEJ,KAAK,CA2EjB;AAED;;;;;GAKG;AACH,0DAHW,MAAM,GACJ,MAAM,CAiBlB;AAED;;;;;;GAMG;AACH,4DAJW,MAAM,wBACN,MAAM,GACJ,MAAM,CAgBlB;AAED;;;;;;;GAOG;AACH,4DANW,MAAM,EAAE,iDACR,MAAM,eACN,MAAM,iBACN,MAAM,SAuEhB;AAGD;;;;;;;;;;;GAWG;AACH,wDATW,MAAM,EAAE,sBACR,MAAM,wBACN,MAAM,GAEJ,KAAK,CAUjB;AA6BD;;;;;;;;;;;;;;;GAeG;AACH,qDATW,MAAM,EAAE,sBACR,MAAM,GAEJ,KAAK,CAgCjB;AAED;;;;;GAKG;AACH,8BAHa,MAAM,CAKlB;AAGD;;;;;;;;;;;;GAYG;AACH,2CARW,MAAM,WACN,MAAM,YACN,MAAM,0BACN,MAAM,OAoBhB;AAED;;;;;;;;;;;;GAYG;AACH,2CARW,MAAM,WACN,MAAM,YACN,MAAM,0BACN,MAAM,OAShB;AAED;;;;;GAKG;AACH,6BAHa,MAAM,EAAE,CAKpB;AAED;;;;;GAKG;AACH,+CAEC;AAED;;;;;GAKG;AACH,0EAEC;AAED;;;;;GAKG;AACH,uEAEC;AAED;;;;;GAKG;AACH,4CAEC;AAED;;;;;GAKG;AACH,0CAEC;AAED;;;;;GAKG;AACH,iDAEC;AAED;;;;;GAKG;AACH,iCAHa,MAAM,CAKlB;AAED;;;;;GAKG;AACH,oEAIC;AAGD;;;;;GAKG;AACH,qCAHa,MAAM,CAKlB;AAED;;;;;GAKG;AACH,iDAEC;AAED;;;;;GAKG;AACH,8BAHa,MAAM,CAKlB;AAED;;;;;GAKG;AACH,0CAEC;AAED;;;;;GAKG;AACH,iDAHa,MAAM,CAKlB;AAED;;;;;GAKG;AACH,6DAOC;AAED;;;;GAIG;AACH,iCAFa,MAAM,CAIlB;AAED;;;;;GAKG;AACH,8CAEC;AAvpCD;;;;aAPG,OAAO,kCAAkC,OAAO,sEAOO;AAC1D,mFAA0D;AAC1D;;;;;EAAgE;AAOhE;;;;mIAAwD;AACxD,kFAAwD;AAiIjD,yFA+KJ;AAOI,0FAFM,MAAM,CAOlB;AAcM,iDAUJ"}
@@ -973,6 +973,15 @@ export function useSetLoaders() {
973
973
  export function useMergeCoordination() {
974
974
  return useViewConfigStore(state => state.mergeCoordination);
975
975
  }
976
+ /**
977
+ * Obtain the current view config object from
978
+ * the global app state.
979
+ * @returns {object} The view config object
980
+ * in the `useViewConfigStore` store.
981
+ */
982
+ export function useViewConfig() {
983
+ return useViewConfigStore(state => state.viewConfig);
984
+ }
976
985
  /**
977
986
  * Obtain the view config setter function from
978
987
  * the global app state.
@@ -1 +1 @@
1
- {"version":3,"file":"VitessceGridLayout.d.ts","sourceRoot":"","sources":["../../src/vitessce-grid-layout/VitessceGridLayout.js"],"names":[],"mappings":"AAeA,oDA8LC"}
1
+ {"version":3,"file":"VitessceGridLayout.d.ts","sourceRoot":"","sources":["../../src/vitessce-grid-layout/VitessceGridLayout.js"],"names":[],"mappings":"AAgBA,oDAwLC"}
@@ -3,6 +3,7 @@ import React, { useState, useMemo, useCallback } from 'react';
3
3
  import { Responsive, WidthProvider } from 'react-grid-layout-with-lodash';
4
4
  import { isEqual } from 'lodash-es';
5
5
  import { getMaxRows, resolveLayout } from './layout-utils.js';
6
+ import { PageModeViewContext } from '../contexts.js';
6
7
  const ResponsiveGridLayout = WidthProvider(Responsive);
7
8
  class ResponsiveHeightGridLayout extends ResponsiveGridLayout {
8
9
  componentDidUpdate(prevProps) {
@@ -97,38 +98,26 @@ export function VitessceGridLayout(props) {
97
98
  const saveCurrentLayouts = useCallback(() => {
98
99
  setLastValidGridLayouts(gridLayouts);
99
100
  }, [gridLayouts]);
100
- const layoutChildren = useMemo(() => {
101
+ const [layoutChildren, pageModeViews] = useMemo(() => {
101
102
  const gridComponentsValues = Object.values(gridComponents);
102
- if (pageMode) {
103
- // This is pageMode, so we render based on the children.
104
- return children.map((child) => {
105
- const childId = child.props.id;
106
- const v = gridComponentsValues.find(el => el.uid === childId);
107
- if (!v) {
108
- // The child did not map to any view in the config, so return as-is.
109
- return child;
110
- }
111
- // The child mapped to a view in the config,
112
- // so we want to render the corresponding component as a subchild.
113
- const Component = getComponent(v.component);
114
- return React.cloneElement(child, { key: v.uid }, (_jsx("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: _jsx(Component, { ...v.props, uuid: v.uid, coordinationScopes: v.coordinationScopes, coordinationScopesBy: v.coordinationScopesBy, theme: theme, removeGridComponent: null }) })));
115
- });
116
- }
103
+ const contextValue = Object.fromEntries(gridComponentsValues.map((v) => {
104
+ const Component = getComponent(v.component);
105
+ return [v.uid, () => (_jsx("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: _jsx(Component, { ...v.props, uuid: v.uid, coordinationScopes: v.coordinationScopes, coordinationScopesBy: v.coordinationScopesBy, theme: theme, removeGridComponent: null }) }, v.uid))];
106
+ }));
117
107
  // Not pageMode, so no children to render.
118
108
  // We render the grid components based on the layout information.
119
- return gridComponentsValues.map((v) => {
120
- const Component = getComponent(v.component);
121
- const removeGridComponent = () => {
122
- onRemoveComponent(v.uid);
123
- };
124
- return (_jsx("div", { children: _jsx(Component, { ...v.props, uuid: v.uid, coordinationScopes: v.coordinationScopes, coordinationScopesBy: v.coordinationScopesBy, theme: theme, removeGridComponent: removeGridComponent }) }, v.uid));
125
- });
109
+ return [gridComponentsValues.map((v) => {
110
+ const Component = getComponent(v.component);
111
+ const removeGridComponent = () => {
112
+ onRemoveComponent(v.uid);
113
+ };
114
+ return (_jsx("div", { children: _jsx(Component, { ...v.props, uuid: v.uid, coordinationScopes: v.coordinationScopes, coordinationScopesBy: v.coordinationScopesBy, theme: theme, removeGridComponent: removeGridComponent }) }, v.uid));
115
+ }), contextValue];
126
116
  }, [gridComponents, getComponent, onRemoveComponent, theme]);
127
117
  return (pageMode
128
- ? layoutChildren
129
- : ((gridLayouts && gridComponents && gridBreakpoints && gridCols) && (_jsxs(_Fragment, { children: [style, _jsx(ResponsiveHeightGridLayout, { className: "layout", cols: gridCols, layouts: tempGridLayouts || gridLayouts, breakpoints: gridBreakpoints, height: height, rowHeight: rowHeight
130
- || ((window.innerHeight - 2 * padding - (maxRows - 1) * margin)
131
- / maxRows), containerPadding: containerPadding, margin: margin, draggableHandle: draggableHandle, onLayoutChange: onLayoutChange, isBounded: isBounded, onResizeStart: saveCurrentLayouts, onDragStart: saveCurrentLayouts, onResize: onResize, onResizeStop: onResizeStop, children: layoutChildren })] }))));
118
+ ? (_jsx(PageModeViewContext.Provider, { value: pageModeViews, children: children })) : ((gridLayouts && gridComponents && gridBreakpoints && gridCols) && (_jsxs(_Fragment, { children: [style, _jsx(ResponsiveHeightGridLayout, { className: "layout", cols: gridCols, layouts: tempGridLayouts || gridLayouts, breakpoints: gridBreakpoints, height: height, rowHeight: rowHeight
119
+ || ((window.innerHeight - 2 * padding - (maxRows - 1) * margin)
120
+ / maxRows), containerPadding: containerPadding, margin: margin, draggableHandle: draggableHandle, onLayoutChange: onLayoutChange, isBounded: isBounded, onResizeStart: saveCurrentLayouts, onDragStart: saveCurrentLayouts, onResize: onResize, onResizeStop: onResizeStop, children: layoutChildren })] }))));
132
121
  }
133
122
  VitessceGridLayout.defaultProps = {
134
123
  padding: 10,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitessce/vit-s",
3
- "version": "3.4.12",
4
- "author": "Gehlenborg Lab",
3
+ "version": "3.5.0",
4
+ "author": "HIDIVE Lab at HMS",
5
5
  "homepage": "http://vitessce.io",
6
6
  "repository": {
7
7
  "type": "git",
@@ -29,12 +29,12 @@
29
29
  "uuid": "^9.0.0",
30
30
  "zustand": "^3.5.10",
31
31
  "react-aria": "^3.28.0",
32
- "@vitessce/constants-internal": "3.4.12",
33
- "@vitessce/plugins": "3.4.12",
34
- "@vitessce/schemas": "3.4.12",
35
- "@vitessce/utils": "3.4.12",
36
- "@vitessce/sets-utils": "3.4.12",
37
- "@vitessce/config": "3.4.12"
32
+ "@vitessce/constants-internal": "3.5.0",
33
+ "@vitessce/plugins": "3.5.0",
34
+ "@vitessce/schemas": "3.5.0",
35
+ "@vitessce/utils": "3.5.0",
36
+ "@vitessce/sets-utils": "3.5.0",
37
+ "@vitessce/config": "3.5.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@testing-library/jest-dom": "^5.16.4",
@@ -42,7 +42,7 @@
42
42
  "react": "^18.0.0",
43
43
  "vite": "^4.3.0",
44
44
  "vitest": "^0.32.2",
45
- "@vitessce/types": "3.4.12"
45
+ "@vitessce/types": "3.5.0"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
package/src/VitS.js CHANGED
@@ -99,15 +99,6 @@ export function VitS(props) {
99
99
  () => (coordinationTypesProp || []),
100
100
  [coordinationTypesProp],
101
101
  );
102
- const asyncFunctions = useMemo(
103
- () => Object.fromEntries(
104
- // Convert the array of PluginAsyncFunction instances to a mapping
105
- // from function type strings to async functions.
106
- asyncFunctionsProp?.map(p => ([p.functionType, p.asyncFunction]))
107
- || [],
108
- ),
109
- [asyncFunctionsProp],
110
- );
111
102
 
112
103
  const generateClassName = useMemo(() => createGenerateClassName(uid), [uid]);
113
104
 
@@ -196,8 +187,23 @@ export function VitS(props) {
196
187
  retry: 2,
197
188
  },
198
189
  },
190
+ // TODO: should the queryClient be shared? Or have an option to be shared
191
+ // (e.g., based on remountOnUidChange)?
199
192
  }), [configKey]);
200
193
 
194
+ const asyncFunctions = useMemo(
195
+ () => ({
196
+ queryClient,
197
+ asyncFunctions: Object.fromEntries(
198
+ // Convert the array of PluginAsyncFunction instances to a mapping
199
+ // from function type strings to async functions.
200
+ asyncFunctionsProp?.map(p => ([p.functionType, p.asyncFunction]))
201
+ || [],
202
+ ),
203
+ }),
204
+ [asyncFunctionsProp, queryClient],
205
+ );
206
+
201
207
  // Emit the upgraded/initialized view config
202
208
  // to onConfigChange if necessary.
203
209
  useEffect(() => {
package/src/contexts.js CHANGED
@@ -2,11 +2,39 @@
2
2
  import { createContext, useContext } from 'react';
3
3
 
4
4
  export const AsyncFunctionsContext = createContext({
5
- // This will be an object where keys are function types
5
+ // {
6
+ // asyncFunctions: { [functionType: string]: asyncFunction },
7
+ // queryClient: QueryClient from react-query
8
+ // }
9
+ // asyncFunctions will be an object where keys are function types
6
10
  // (as strings) and values are the async functions.
7
11
  });
8
12
 
9
13
  export function useAsyncFunction(functionType) {
10
14
  const context = useContext(AsyncFunctionsContext);
11
- return context?.[functionType];
15
+ const origFunction = context?.asyncFunctions?.[functionType];
16
+ const queryClient = context?.queryClient;
17
+ // We use partial function application here, so that the caller
18
+ // does not need to worry about passing in the queryClient.
19
+ const partialFunction = (...args) => origFunction({ queryClient }, ...args);
20
+ return partialFunction;
21
+ }
22
+
23
+ export const PageModeViewContext = createContext({
24
+ // Keys are view UIDs and values are
25
+ // the corresponding React components.
26
+ });
27
+
28
+ function ensureComponent(component) {
29
+ if (!component) {
30
+ return () => null;
31
+ }
32
+ return component;
33
+ }
34
+
35
+ export function usePageModeView(uid) {
36
+ const context = useContext(PageModeViewContext);
37
+ // TODO: Is there any case where we would want to
38
+ // throw an error instead?
39
+ return ensureComponent(context?.[uid]);
12
40
  }
package/src/hooks.js CHANGED
@@ -221,28 +221,37 @@ export function useUint8ObsFeatureMatrix({ obsFeatureMatrix }) {
221
221
  /**
222
222
  * Normalize a feature selection (data for selected
223
223
  * columns of an obsFeatureMatrix) to a Uint8Array.
224
- * @param {array|null} expressionData The expressionData
224
+ * @param {Float32Array[] | null} expressionData The expressionData
225
225
  * returned by the useFeatureSelection hook,
226
226
  * where each element corresponds to an
227
227
  * array of values for a selected feature.
228
- * @returns {array} A tuple [normData, extents] where
229
- * normData is an array of Uint8Arrays (or null), and extents is
230
- * an array of [min, max] values for each feature (or null).
228
+ * @returns {{
229
+ * normData: Uint8Array[] | null,
230
+ * extents: [number, number][] | null,
231
+ * missing: number[]
232
+ * }} An object tuple {normData, extents, missing} where
233
+ * normData is an array of Uint8Arrays (or null),
234
+ * extents is an array of [min, max] values for each feature (or null), and
235
+ * missing is an array of numbers (between 0 and 1) for each feature (or null).
231
236
  */
232
237
  export function useUint8FeatureSelection(expressionData) {
233
238
  return useMemo(() => {
234
- if (expressionData && expressionData[0]) {
235
- const extents = expressionData.map(arr => extent(arr));
236
- const normData = expressionData.map((arr, i) => {
237
- const [min, max] = extents[i];
238
- const ratio = 255 / (max - min);
239
- return new Uint8Array(
240
- arr.map(j => Math.floor((j - min) * ratio)),
241
- );
242
- });
243
- return [normData, extents];
239
+ if (expressionData?.[0] == null) {
240
+ return { normData: null, extents: null, missing: null };
244
241
  }
245
- return [null, null];
242
+ const extents = expressionData.map(arr => extent(arr));
243
+ const normData = expressionData.map((arr, i) => {
244
+ const [min, max] = extents[i];
245
+ const ratio = 255 / (max - min);
246
+ return new Uint8Array(
247
+ arr.map(j => Math.floor((j - min) * ratio)),
248
+ );
249
+ });
250
+ const missing = expressionData.map((arr) => {
251
+ const numMissing = arr.reduce((prev, curr) => (Number.isNaN(curr) ? prev + 1 : prev), 0);
252
+ return numMissing / arr.length;
253
+ });
254
+ return { normData, extents, missing };
246
255
  }, [expressionData]);
247
256
  }
248
257
 
package/src/index.js CHANGED
@@ -35,6 +35,8 @@ export {
35
35
  useMatchingLoader,
36
36
  useViewConfigStore,
37
37
  useViewConfigStoreApi,
38
+ useViewConfig,
39
+ useSetViewConfig,
38
40
  useComponentHover,
39
41
  useSetComponentHover,
40
42
  useComponentViewInfo,
@@ -105,4 +107,4 @@ export {
105
107
  usePlotOptionsStyles,
106
108
  } from './shared-plot-options/index.js';
107
109
  export { logConfig } from './view-config-utils.js';
108
- export { useAsyncFunction } from './contexts.js';
110
+ export { useAsyncFunction, usePageModeView } from './contexts.js';
@@ -1091,6 +1091,16 @@ export function useMergeCoordination() {
1091
1091
  return useViewConfigStore(state => state.mergeCoordination);
1092
1092
  }
1093
1093
 
1094
+ /**
1095
+ * Obtain the current view config object from
1096
+ * the global app state.
1097
+ * @returns {object} The view config object
1098
+ * in the `useViewConfigStore` store.
1099
+ */
1100
+ export function useViewConfig() {
1101
+ return useViewConfigStore(state => state.viewConfig);
1102
+ }
1103
+
1094
1104
  /**
1095
1105
  * Obtain the view config setter function from
1096
1106
  * the global app state.
@@ -1103,6 +1113,7 @@ export function useSetViewConfig(viewConfigStoreApi) {
1103
1113
  return setViewConfig;
1104
1114
  }
1105
1115
 
1116
+
1106
1117
  /**
1107
1118
  * Obtain the component hover value from
1108
1119
  * the global app state.
@@ -2,6 +2,7 @@ import React, { useState, useMemo, useCallback } from 'react';
2
2
  import { Responsive, WidthProvider } from 'react-grid-layout-with-lodash';
3
3
  import { isEqual } from 'lodash-es';
4
4
  import { getMaxRows, resolveLayout } from './layout-utils.js';
5
+ import { PageModeViewContext } from '../contexts.js';
5
6
 
6
7
  const ResponsiveGridLayout = WidthProvider(Responsive);
7
8
 
@@ -122,37 +123,28 @@ export function VitessceGridLayout(props) {
122
123
  setLastValidGridLayouts(gridLayouts);
123
124
  }, [gridLayouts]);
124
125
 
125
- const layoutChildren = useMemo(() => {
126
+ const [layoutChildren, pageModeViews] = useMemo(() => {
126
127
  const gridComponentsValues = Object.values(gridComponents);
127
- if (pageMode) {
128
- // This is pageMode, so we render based on the children.
129
- return children.map((child) => {
130
- const childId = child.props.id;
131
- const v = gridComponentsValues.find(el => el.uid === childId);
132
- if (!v) {
133
- // The child did not map to any view in the config, so return as-is.
134
- return child;
135
- }
136
- // The child mapped to a view in the config,
137
- // so we want to render the corresponding component as a subchild.
138
- const Component = getComponent(v.component);
139
- return React.cloneElement(child, { key: v.uid }, (
140
- <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
141
- <Component
142
- {... v.props}
143
- uuid={v.uid}
144
- coordinationScopes={v.coordinationScopes}
145
- coordinationScopesBy={v.coordinationScopesBy}
146
- theme={theme}
147
- removeGridComponent={null}
148
- />
149
- </div>
150
- ));
151
- });
152
- }
128
+
129
+ const contextValue = Object.fromEntries(gridComponentsValues.map((v) => {
130
+ const Component = getComponent(v.component);
131
+ return [v.uid, () => (
132
+ <div key={v.uid} style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
133
+ <Component
134
+ {... v.props}
135
+ uuid={v.uid}
136
+ coordinationScopes={v.coordinationScopes}
137
+ coordinationScopesBy={v.coordinationScopesBy}
138
+ theme={theme}
139
+ removeGridComponent={null}
140
+ />
141
+ </div>
142
+ )];
143
+ }));
144
+
153
145
  // Not pageMode, so no children to render.
154
146
  // We render the grid components based on the layout information.
155
- return gridComponentsValues.map((v) => {
147
+ return [gridComponentsValues.map((v) => {
156
148
  const Component = getComponent(v.component);
157
149
  const removeGridComponent = () => {
158
150
  onRemoveComponent(v.uid);
@@ -169,12 +161,15 @@ export function VitessceGridLayout(props) {
169
161
  />
170
162
  </div>
171
163
  );
172
- });
164
+ }), contextValue];
173
165
  }, [gridComponents, getComponent, onRemoveComponent, theme]);
174
166
 
175
167
  return (pageMode
176
- ? layoutChildren
177
- : ((gridLayouts && gridComponents && gridBreakpoints && gridCols) && (
168
+ ? (
169
+ <PageModeViewContext.Provider value={pageModeViews}>
170
+ {children}
171
+ </PageModeViewContext.Provider>
172
+ ) : ((gridLayouts && gridComponents && gridBreakpoints && gridCols) && (
178
173
  <>
179
174
  {style}
180
175
  <ResponsiveHeightGridLayout