@vitessce/scatterplot-embedding 3.8.9 → 3.8.10

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.
@@ -1,5 +1,5 @@
1
1
  import { i as inflate_1 } from "./pako.esm-SxljTded.js";
2
- import { B as BaseDecoder } from "./index-DYO3tfis.js";
2
+ import { B as BaseDecoder } from "./index-BgrJUaQb.js";
3
3
  class DeflateDecoder extends BaseDecoder {
4
4
  decodeBlock(buffer) {
5
5
  return inflate_1(new Uint8Array(buffer)).buffer;
@@ -123401,22 +123401,22 @@ function addDecoder(cases, importFn) {
123401
123401
  }
123402
123402
  cases.forEach((c2) => registry$1.set(c2, importFn));
123403
123403
  }
123404
- addDecoder([void 0, 1], () => import("./raw-xM2f2nBA.js").then((m2) => m2.default));
123405
- addDecoder(5, () => import("./lzw-B2nz96hr.js").then((m2) => m2.default));
123404
+ addDecoder([void 0, 1], () => import("./raw-BJDJq5QK.js").then((m2) => m2.default));
123405
+ addDecoder(5, () => import("./lzw-BwAlpbiv.js").then((m2) => m2.default));
123406
123406
  addDecoder(6, () => {
123407
123407
  throw new Error("old style JPEG compression is not supported.");
123408
123408
  });
123409
- addDecoder(7, () => import("./jpeg-BBSvSY8b.js").then((m2) => m2.default));
123410
- addDecoder([8, 32946], () => import("./deflate-YIIHzd8y.js").then((m2) => m2.default));
123411
- addDecoder(32773, () => import("./packbits-JodEChjB.js").then((m2) => m2.default));
123409
+ addDecoder(7, () => import("./jpeg-V5Wg0DTH.js").then((m2) => m2.default));
123410
+ addDecoder([8, 32946], () => import("./deflate-DV84laLg.js").then((m2) => m2.default));
123411
+ addDecoder(32773, () => import("./packbits-BzaxDQbT.js").then((m2) => m2.default));
123412
123412
  addDecoder(
123413
123413
  34887,
123414
- () => import("./lerc-DsiDEIic.js").then(async (m2) => {
123414
+ () => import("./lerc-LFwYjack.js").then(async (m2) => {
123415
123415
  await m2.zstd.init();
123416
123416
  return m2;
123417
123417
  }).then((m2) => m2.default)
123418
123418
  );
123419
- addDecoder(50001, () => import("./webimage-Bk5jq0He.js").then((m2) => m2.default));
123419
+ addDecoder(50001, () => import("./webimage-6o_3zIHO.js").then((m2) => m2.default));
123420
123420
  function decodeRowAcc(row, stride) {
123421
123421
  let length2 = row.length - stride;
123422
123422
  let offset2 = 0;
@@ -150493,8 +150493,14 @@ function forceCollideRects() {
150493
150493
  };
150494
150494
  return force;
150495
150495
  }
150496
- function getPointSizeDevicePixels(devicePixelRatio, zoom, xRange, yRange, width2, height2) {
150497
- const pointSize = 5e-4;
150496
+ const BASE_POINT_SIZE = 5;
150497
+ const LARGE_DATASET_CELL_COUNT = 1e4;
150498
+ const SMALL_DATASET_CELL_COUNT = 100;
150499
+ function getInitialPointSize(numCells = LARGE_DATASET_CELL_COUNT) {
150500
+ return BASE_POINT_SIZE / clamp$4(numCells, SMALL_DATASET_CELL_COUNT, LARGE_DATASET_CELL_COUNT);
150501
+ }
150502
+ function getPointSizeDevicePixels(devicePixelRatio, zoom, xRange, yRange, width2, height2, numCells) {
150503
+ const pointSize = getInitialPointSize(numCells);
150498
150504
  const pointScreenSizeMax = 10;
150499
150505
  const pointScreenSizeMin = 1 / devicePixelRatio;
150500
150506
  const scaleFactor = 2 ** zoom;
@@ -152052,40 +152058,51 @@ function number$2(x) {
152052
152058
  const ascendingBisect = bisector(ascending);
152053
152059
  const bisectRight = ascendingBisect.right;
152054
152060
  bisector(number$2).center;
152055
- var e10 = Math.sqrt(50), e5 = Math.sqrt(10), e2 = Math.sqrt(2);
152061
+ const e10 = Math.sqrt(50), e5 = Math.sqrt(10), e2 = Math.sqrt(2);
152062
+ function tickSpec(start, stop, count2) {
152063
+ const step = (stop - start) / Math.max(0, count2), power = Math.floor(Math.log10(step)), error2 = step / Math.pow(10, power), factor = error2 >= e10 ? 10 : error2 >= e5 ? 5 : error2 >= e2 ? 2 : 1;
152064
+ let i1, i2, inc;
152065
+ if (power < 0) {
152066
+ inc = Math.pow(10, -power) / factor;
152067
+ i1 = Math.round(start * inc);
152068
+ i2 = Math.round(stop * inc);
152069
+ if (i1 / inc < start) ++i1;
152070
+ if (i2 / inc > stop) --i2;
152071
+ inc = -inc;
152072
+ } else {
152073
+ inc = Math.pow(10, power) * factor;
152074
+ i1 = Math.round(start / inc);
152075
+ i2 = Math.round(stop / inc);
152076
+ if (i1 * inc < start) ++i1;
152077
+ if (i2 * inc > stop) --i2;
152078
+ }
152079
+ if (i2 < i1 && 0.5 <= count2 && count2 < 2) return tickSpec(start, stop, count2 * 2);
152080
+ return [i1, i2, inc];
152081
+ }
152056
152082
  function ticks(start, stop, count2) {
152057
- var reverse, i2 = -1, n2, ticks2, step;
152058
152083
  stop = +stop, start = +start, count2 = +count2;
152059
- if (start === stop && count2 > 0) return [start];
152060
- if (reverse = stop < start) n2 = start, start = stop, stop = n2;
152061
- if ((step = tickIncrement(start, stop, count2)) === 0 || !isFinite(step)) return [];
152062
- if (step > 0) {
152063
- let r0 = Math.round(start / step), r1 = Math.round(stop / step);
152064
- if (r0 * step < start) ++r0;
152065
- if (r1 * step > stop) --r1;
152066
- ticks2 = new Array(n2 = r1 - r0 + 1);
152067
- while (++i2 < n2) ticks2[i2] = (r0 + i2) * step;
152084
+ if (!(count2 > 0)) return [];
152085
+ if (start === stop) return [start];
152086
+ const reverse = stop < start, [i1, i2, inc] = reverse ? tickSpec(stop, start, count2) : tickSpec(start, stop, count2);
152087
+ if (!(i2 >= i1)) return [];
152088
+ const n2 = i2 - i1 + 1, ticks2 = new Array(n2);
152089
+ if (reverse) {
152090
+ if (inc < 0) for (let i3 = 0; i3 < n2; ++i3) ticks2[i3] = (i2 - i3) / -inc;
152091
+ else for (let i3 = 0; i3 < n2; ++i3) ticks2[i3] = (i2 - i3) * inc;
152068
152092
  } else {
152069
- step = -step;
152070
- let r0 = Math.round(start * step), r1 = Math.round(stop * step);
152071
- if (r0 / step < start) ++r0;
152072
- if (r1 / step > stop) --r1;
152073
- ticks2 = new Array(n2 = r1 - r0 + 1);
152074
- while (++i2 < n2) ticks2[i2] = (r0 + i2) / step;
152075
- }
152076
- if (reverse) ticks2.reverse();
152093
+ if (inc < 0) for (let i3 = 0; i3 < n2; ++i3) ticks2[i3] = (i1 + i3) / -inc;
152094
+ else for (let i3 = 0; i3 < n2; ++i3) ticks2[i3] = (i1 + i3) * inc;
152095
+ }
152077
152096
  return ticks2;
152078
152097
  }
152079
152098
  function tickIncrement(start, stop, count2) {
152080
- var step = (stop - start) / Math.max(0, count2), power = Math.floor(Math.log(step) / Math.LN10), error2 = step / Math.pow(10, power);
152081
- return power >= 0 ? (error2 >= e10 ? 10 : error2 >= e5 ? 5 : error2 >= e2 ? 2 : 1) * Math.pow(10, power) : -Math.pow(10, -power) / (error2 >= e10 ? 10 : error2 >= e5 ? 5 : error2 >= e2 ? 2 : 1);
152099
+ stop = +stop, start = +start, count2 = +count2;
152100
+ return tickSpec(start, stop, count2)[2];
152082
152101
  }
152083
152102
  function tickStep(start, stop, count2) {
152084
- var step0 = Math.abs(stop - start) / Math.max(0, count2), step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)), error2 = step0 / step1;
152085
- if (error2 >= e10) step1 *= 10;
152086
- else if (error2 >= e5) step1 *= 5;
152087
- else if (error2 >= e2) step1 *= 2;
152088
- return stop < start ? -step1 : step1;
152103
+ stop = +stop, start = +start, count2 = +count2;
152104
+ const reverse = stop < start, inc = reverse ? tickIncrement(stop, start, count2) : tickIncrement(start, stop, count2);
152105
+ return (reverse ? -1 : 1) * (inc < 0 ? 1 / -inc : inc);
152089
152106
  }
152090
152107
  function initRange(domain, range2) {
152091
152108
  switch (arguments.length) {
@@ -153167,6 +153184,9 @@ function ramp(color2, n2 = 256) {
153167
153184
  canvas.width = n2;
153168
153185
  canvas.height = 1;
153169
153186
  const context = canvas.getContext("2d");
153187
+ if (!context) {
153188
+ throw new Error("Could not get 2d context from canvas");
153189
+ }
153170
153190
  for (let i2 = 0; i2 < n2; ++i2) {
153171
153191
  context.fillStyle = color2(i2 / (n2 - 1));
153172
153192
  context.fillRect(i2, 0, 1, 1);
@@ -153185,6 +153205,8 @@ function getXlinkHref(cmap) {
153185
153205
  }
153186
153206
  const useStyles = makeStyles()(() => ({
153187
153207
  legend: {
153208
+ position: "relative",
153209
+ // Needed for absolute positioning of slider overlay
153188
153210
  top: "2px",
153189
153211
  right: "2px",
153190
153212
  fontSize: "10px !important",
@@ -153216,6 +153238,74 @@ const useStyles = makeStyles()(() => ({
153216
153238
  },
153217
153239
  legendInvisible: {
153218
153240
  display: "none"
153241
+ },
153242
+ sliderContainer: {
153243
+ position: "absolute",
153244
+ // Position at the colormap location: top offset = titleHeight
153245
+ top: "10px",
153246
+ // titleHeight
153247
+ left: "2px",
153248
+ // Account for parent padding
153249
+ width: "calc(100% - 4px)",
153250
+ // Account for left and right padding
153251
+ height: "8px",
153252
+ // rectHeight
153253
+ "&:hover $sliderThumb": {
153254
+ opacity: 1
153255
+ }
153256
+ },
153257
+ sliderRoot: {
153258
+ position: "absolute",
153259
+ top: 0,
153260
+ left: 0,
153261
+ width: "100%",
153262
+ height: "8px",
153263
+ // rectHeight
153264
+ padding: 0,
153265
+ "& .MuiSlider-rail": {
153266
+ display: "none"
153267
+ },
153268
+ "& .MuiSlider-track": {
153269
+ display: "none"
153270
+ },
153271
+ "& .MuiSlider-valueLabel": {
153272
+ fontSize: "9px",
153273
+ padding: "2px 4px",
153274
+ backgroundColor: "rgb(0, 0, 0)",
153275
+ borderRadius: "2px"
153276
+ }
153277
+ },
153278
+ sliderThumb: {
153279
+ width: "4px",
153280
+ height: "12px",
153281
+ borderRadius: "2px",
153282
+ backgroundColor: "white",
153283
+ border: "1px solid black",
153284
+ opacity: 0,
153285
+ transition: "opacity 0.15s ease-in-out",
153286
+ "&:hover, &.Mui-focusVisible": {
153287
+ boxShadow: "0 0 0 4px rgba(0, 0, 0, 0.16)",
153288
+ opacity: 1
153289
+ },
153290
+ "&.Mui-active": {
153291
+ boxShadow: "0 0 0 6px rgba(0, 0, 0, 0.16)",
153292
+ opacity: 1
153293
+ }
153294
+ },
153295
+ colormapImage: {
153296
+ position: "absolute",
153297
+ top: "2px",
153298
+ height: "6px",
153299
+ // rectHeight
153300
+ pointerEvents: "none"
153301
+ },
153302
+ grayTrack: {
153303
+ position: "absolute",
153304
+ top: "2px",
153305
+ height: "6px",
153306
+ // rectHeight
153307
+ backgroundColor: "rgba(128, 128, 128, 0.5)",
153308
+ pointerEvents: "none"
153219
153309
  }
153220
153310
  }));
153221
153311
  const titleHeight = 10;
@@ -153225,21 +153315,23 @@ const rectMarginX = 2;
153225
153315
  function combineExtents(extents, featureAggregationStrategy) {
153226
153316
  if (Array.isArray(extents)) {
153227
153317
  if (Array.isArray(extents == null ? void 0 : extents[0])) {
153318
+ const extentsArray = extents;
153228
153319
  if (featureAggregationStrategy === "first") {
153229
- return extents[0];
153320
+ return extentsArray[0];
153230
153321
  }
153231
153322
  if (featureAggregationStrategy === "last") {
153232
- return extents.at(-1);
153323
+ return extentsArray.at(-1) || null;
153233
153324
  }
153234
153325
  if (typeof featureAggregationStrategy === "number") {
153235
153326
  const i2 = featureAggregationStrategy;
153236
- return extents[i2];
153327
+ return extentsArray[i2];
153237
153328
  }
153238
153329
  if (featureAggregationStrategy === "sum") {
153239
- return extents.reduce((a2, h) => [a2[0] + h[0], a2[1] + h[1]]);
153330
+ return extentsArray.reduce((a2, h) => [a2[0] + h[0], a2[1] + h[1]]);
153240
153331
  }
153241
153332
  if (featureAggregationStrategy === "mean") {
153242
- return extents.reduce((a2, h) => [a2[0] + h[0], a2[1] + h[1]]).map((v) => v / extents.length);
153333
+ const sum2 = extentsArray.reduce((a2, h) => [a2[0] + h[0], a2[1] + h[1]]);
153334
+ return [sum2[0] / extentsArray.length, sum2[1] / extentsArray.length];
153243
153335
  }
153244
153336
  } else {
153245
153337
  return extents;
@@ -153253,7 +153345,7 @@ function combineMissings(missings, featureAggregationStrategy) {
153253
153345
  return missings[0];
153254
153346
  }
153255
153347
  if (featureAggregationStrategy === "last") {
153256
- return missings.at(-1);
153348
+ return missings.at(-1) || null;
153257
153349
  }
153258
153350
  if (typeof featureAggregationStrategy === "number") {
153259
153351
  const i2 = featureAggregationStrategy;
@@ -153269,52 +153361,127 @@ function combineMissings(missings, featureAggregationStrategy) {
153269
153361
  return null;
153270
153362
  }
153271
153363
  function Legend(props) {
153272
- const { visible: visibleProp, positionRelative = false, highContrast = false, obsType, featureValueType, considerSelections = true, obsColorEncoding, featureSelection, featureLabelsMap, featureValueColormap, featureValueColormapRange, spatialChannelColor, spatialLayerColor, obsSetSelection, obsSetColor, featureAggregationStrategy, extent: extent2, missing, width: width2 = 100, height: height2 = 36, maxHeight: maxHeight2 = null, theme, showObsLabel = false, pointsVisible = true, contoursVisible = false, contoursFilled, contourPercentiles, contourThresholds } = props;
153273
- const svgRef = useRef();
153364
+ const {
153365
+ visible: visibleProp,
153366
+ positionRelative = false,
153367
+ highContrast = false,
153368
+ obsType,
153369
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
153370
+ featureType: _featureType = void 0,
153371
+ // Unused but accepted for API compatibility
153372
+ featureValueType,
153373
+ considerSelections = true,
153374
+ obsColorEncoding,
153375
+ featureSelection,
153376
+ featureLabelsMap,
153377
+ featureValueColormap,
153378
+ featureValueColormapRange,
153379
+ setFeatureValueColormapRange,
153380
+ spatialChannelColor,
153381
+ spatialLayerColor,
153382
+ obsSetSelection,
153383
+ obsSetColor,
153384
+ featureAggregationStrategy,
153385
+ extent: extent2,
153386
+ missing,
153387
+ width: width2 = 100,
153388
+ height: height2 = 36,
153389
+ maxHeight: maxHeight2 = null,
153390
+ theme,
153391
+ showObsLabel = false,
153392
+ pointsVisible = true,
153393
+ contoursVisible = false,
153394
+ contoursFilled,
153395
+ contourPercentiles,
153396
+ contourThresholds
153397
+ } = props;
153398
+ const svgRef = useRef(null);
153274
153399
  const { classes } = useStyles();
153400
+ const [localRange, setLocalRange] = useState(featureValueColormapRange);
153401
+ useEffect(() => {
153402
+ setLocalRange(featureValueColormapRange);
153403
+ }, [featureValueColormapRange]);
153404
+ const debouncedSetRange = useMemo(() => setFeatureValueColormapRange ? debounce$1((value) => {
153405
+ setFeatureValueColormapRange(value);
153406
+ }, 5, { leading: false, trailing: true }) : null, [setFeatureValueColormapRange]);
153407
+ useEffect(() => () => {
153408
+ if (debouncedSetRange) {
153409
+ debouncedSetRange.cancel();
153410
+ }
153411
+ }, [debouncedSetRange]);
153412
+ const handleSliderChange = useCallback((_event, newValue) => {
153413
+ const rangeValue = newValue;
153414
+ setLocalRange(rangeValue);
153415
+ if (debouncedSetRange) {
153416
+ debouncedSetRange(rangeValue);
153417
+ }
153418
+ }, [debouncedSetRange]);
153275
153419
  const isDarkTheme = theme === "dark";
153276
153420
  const isStaticColor = obsColorEncoding === "spatialChannelColor" || obsColorEncoding === "spatialLayerColor";
153277
153421
  const isSetColor = obsColorEncoding === "cellSetSelection";
153278
- const layerColor = Array.isArray(spatialLayerColor) && spatialLayerColor.length === 3 ? spatialLayerColor : getDefaultColor(theme);
153279
- const channelColor = Array.isArray(spatialChannelColor) && spatialChannelColor.length === 3 ? spatialChannelColor : getDefaultColor(theme);
153422
+ const layerColor = Array.isArray(spatialLayerColor) && spatialLayerColor.length === 3 ? spatialLayerColor : getDefaultColor(theme ?? "light");
153423
+ const channelColor = Array.isArray(spatialChannelColor) && spatialChannelColor.length === 3 ? spatialChannelColor : getDefaultColor(theme ?? "light");
153280
153424
  const staticColor = obsColorEncoding === "spatialChannelColor" ? channelColor : layerColor;
153281
- const visible = visibleProp && (!considerSelections || obsColorEncoding === "geneSelection" && featureSelection && Array.isArray(featureSelection) && featureSelection.length >= 1 || isSetColor && (obsSetSelection == null ? void 0 : obsSetSelection.length) > 0 && (obsSetColor == null ? void 0 : obsSetColor.length) > 0 || isStaticColor);
153425
+ const visible = visibleProp && (!considerSelections || ["geneSelection", "geneExpression"].includes(obsColorEncoding ?? "") && featureSelection && Array.isArray(featureSelection) && featureSelection.length >= 1 || isSetColor && ((obsSetSelection == null ? void 0 : obsSetSelection.length) ?? 0) > 0 && ((obsSetColor == null ? void 0 : obsSetColor.length) ?? 0) > 0 || isStaticColor);
153282
153426
  const levelZeroNames = useMemo(() => Array.from(new Set((obsSetSelection == null ? void 0 : obsSetSelection.map((setPath) => setPath[0])) || [])), [obsSetSelection]);
153283
- const dynamicHeight = isSetColor && obsSetSelection ? levelZeroNames.length * titleHeight + (obsSetSelection == null ? void 0 : obsSetSelection.length) * (rectHeight + rectMarginY) : height2 + (!pointsVisible && contoursVisible ? 25 : 0);
153427
+ const dynamicHeight = isSetColor && obsSetSelection ? levelZeroNames.length * titleHeight + ((obsSetSelection == null ? void 0 : obsSetSelection.length) ?? 0) * (rectHeight + rectMarginY) : height2 + (!pointsVisible && contoursVisible ? 25 : 0);
153284
153428
  const availHeight = maxHeight2 !== null ? Math.max(0, maxHeight2 - 4) : Infinity;
153285
153429
  const needsScroll = Number.isFinite(availHeight) && dynamicHeight > availHeight + 1;
153286
153430
  useEffect(() => {
153287
153431
  const domElement = svgRef.current;
153432
+ if (!domElement)
153433
+ return;
153288
153434
  const foregroundColor = highContrast ? "black" : isDarkTheme ? "white" : "black";
153289
153435
  const svg = select(domElement);
153290
153436
  svg.selectAll("g").remove();
153291
153437
  svg.attr("width", width2).attr("height", dynamicHeight);
153292
153438
  const g2 = svg.append("g").attr("width", width2).attr("height", dynamicHeight);
153293
- if (!considerSelections || obsColorEncoding === "geneSelection") {
153294
- const [xMin, xMax] = combineExtents(extent2, featureAggregationStrategy) || [0, 1];
153439
+ const showInteractiveSlider2 = setFeatureValueColormapRange && ["geneSelection", "geneExpression"].includes(obsColorEncoding ?? "") && pointsVisible && featureValueColormap;
153440
+ if (!considerSelections || ["geneSelection", "geneExpression"].includes(obsColorEncoding ?? "")) {
153441
+ const combinedExtent = combineExtents(extent2 ?? null, featureAggregationStrategy ?? null) || [0, 1];
153442
+ const [xMin, xMax] = combinedExtent;
153295
153443
  if (featureValueColormap && pointsVisible) {
153296
- const xlinkHref = getXlinkHref(featureValueColormap);
153297
- g2.append("image").attr("x", 0).attr("y", titleHeight).attr("width", width2).attr("height", rectHeight).attr("preserveAspectRatio", "none").attr("href", xlinkHref);
153298
- const [rMin, rMax] = featureValueColormapRange;
153444
+ const xlinkHref2 = getXlinkHref(featureValueColormap);
153445
+ const currentRange = showInteractiveSlider2 ? localRange : featureValueColormapRange;
153446
+ const [rMin, rMax] = currentRange || [0, 1];
153447
+ if (showInteractiveSlider2) ;
153448
+ else if (setFeatureValueColormapRange) {
153449
+ g2.append("image").attr("x", rMin * width2).attr("y", titleHeight).attr("width", (rMax - rMin) * width2).attr("height", rectHeight).attr("preserveAspectRatio", "none").attr("href", xlinkHref2);
153450
+ } else {
153451
+ g2.append("image").attr("x", 0).attr("y", titleHeight).attr("width", width2).attr("height", rectHeight).attr("preserveAspectRatio", "none").attr("href", xlinkHref2);
153452
+ }
153299
153453
  const scaledDataExtent = [
153300
153454
  xMin + (xMax - xMin) * rMin,
153301
153455
  xMax - (xMax - xMin) * (1 - rMax)
153302
153456
  ];
153303
- const x = linear().domain(scaledDataExtent).range([0.5, width2 - 0.5]);
153304
- const axisTicks = g2.append("g").attr("transform", `translate(0,${titleHeight + rectHeight})`).style("font-size", "10px").call(axisBottom(x).tickValues(scaledDataExtent));
153305
- axisTicks.selectAll("line,path").style("stroke", foregroundColor);
153306
- axisTicks.selectAll("text").style("fill", foregroundColor);
153307
- axisTicks.selectAll("text").attr("text-anchor", (d, i2) => i2 === 0 ? "start" : "end");
153308
- } else if (contoursVisible) {
153457
+ let x;
153458
+ if (setFeatureValueColormapRange || showInteractiveSlider2) {
153459
+ x = linear().domain(scaledDataExtent).range([rMin * width2, rMax * width2]);
153460
+ } else {
153461
+ x = linear().domain(scaledDataExtent).range([0, width2]);
153462
+ }
153463
+ if (showInteractiveSlider2) {
153464
+ const xGlobal = linear().domain([xMin, xMax]).range([0, width2 - 4]);
153465
+ const axisTicks = g2.append("g").attr("transform", `translate(0,${titleHeight + rectHeight})`).style("font-size", "10px").call(axisBottom(xGlobal).tickValues([xMin, xMax]));
153466
+ axisTicks.selectAll("line,path").style("stroke", foregroundColor);
153467
+ axisTicks.selectAll("text").style("fill", foregroundColor);
153468
+ axisTicks.selectAll("text").attr("text-anchor", (_d, i2) => i2 === 0 ? "start" : "end");
153469
+ } else {
153470
+ const axisTicks = g2.append("g").attr("transform", `translate(0,${titleHeight + rectHeight})`).style("font-size", "10px").call(axisBottom(x).tickValues(scaledDataExtent));
153471
+ axisTicks.selectAll("line,path").style("stroke", foregroundColor);
153472
+ axisTicks.selectAll("text").style("fill", foregroundColor);
153473
+ axisTicks.selectAll("text").attr("text-anchor", (_d, i2) => i2 === 0 ? "start" : "end");
153474
+ }
153475
+ } else if (contoursVisible && contourPercentiles) {
153309
153476
  const tSize = 12;
153310
153477
  const xPercentile = linear().domain([0, 1]).range([tSize / 2, width2 - tSize / 2 - 2]);
153311
- const axisTicks = g2.append("g").attr("transform", `translate(0,${titleHeight + rectHeight + 15})`).style("font-size", "9px").call(axisBottom(xPercentile).tickValues(contourPercentiles).tickFormat(format(".0%")).tickSizeOuter(0));
153478
+ const axisTicks = g2.append("g").attr("transform", `translate(0,${titleHeight + rectHeight + 15})`).style("font-size", "9px").call(axisBottom(xPercentile).tickValues(contourPercentiles).tickFormat((d) => format(".0%")(d)).tickSizeOuter(0));
153312
153479
  axisTicks.selectAll("line,path").style("stroke", foregroundColor);
153313
153480
  axisTicks.selectAll("text").style("fill", foregroundColor);
153314
153481
  const NEIGHBOR_THRESHOLD = 18;
153315
- const contourPercentages = contourPercentiles.map((x) => x * 100);
153482
+ const contourPercentages = contourPercentiles.map((p) => p * 100);
153316
153483
  if ((contourPercentages == null ? void 0 : contourPercentages[1]) - (contourPercentages == null ? void 0 : contourPercentages[0]) <= NEIGHBOR_THRESHOLD || (contourPercentages == null ? void 0 : contourPercentages[2]) - (contourPercentages == null ? void 0 : contourPercentages[1]) <= NEIGHBOR_THRESHOLD) {
153317
- axisTicks.selectAll("text").attr("transform", (d, i2) => `translate(0,${i2 === 0 || i2 === contourPercentiles.length - 1 ? 0 : 10})`);
153484
+ axisTicks.selectAll("text").attr("transform", (_d, i2) => `translate(0,${i2 === 0 || i2 === contourPercentiles.length - 1 ? 0 : 10})`);
153318
153485
  }
153319
153486
  const triangleGroupG = g2.append("g").attr("transform", `translate(0,${titleHeight + rectHeight + 4})`);
153320
153487
  contourPercentiles.forEach((p, i2) => {
@@ -153324,7 +153491,7 @@ function Legend(props) {
153324
153491
  const thresholdGroupG = g2.append("g").attr("transform", `translate(0,${titleHeight + rectHeight})`);
153325
153492
  const thresholdFormatter = format(".0f");
153326
153493
  contourPercentiles.forEach((p, i2) => {
153327
- const contourThreshold = xMin + (xMax - xMin) * ((contourThresholds == null ? void 0 : contourThresholds[i2]) / 255);
153494
+ const contourThreshold = xMin + (xMax - xMin) * (((contourThresholds == null ? void 0 : contourThresholds[i2]) ?? 0) / 255);
153328
153495
  const thresholdG = thresholdGroupG.append("g").attr("transform", `translate(${xPercentile(p)},0)`).style("font-size", "7px");
153329
153496
  thresholdG.append("text").text(thresholdFormatter(contourThreshold)).style("fill", foregroundColor).attr("text-anchor", "middle");
153330
153497
  });
@@ -153348,9 +153515,9 @@ function Legend(props) {
153348
153515
  y += titleHeight;
153349
153516
  setPaths.forEach((setPath) => {
153350
153517
  var _a3;
153351
- const setColor2 = ((_a3 = obsSetColor == null ? void 0 : obsSetColor.find((d) => isEqual$1(d.path, setPath))) == null ? void 0 : _a3.color) || getDefaultColor(theme);
153518
+ const setColor2 = ((_a3 = obsSetColor == null ? void 0 : obsSetColor.find((d) => isEqual$1(d.path, setPath))) == null ? void 0 : _a3.color) || getDefaultColor(theme ?? "light");
153352
153519
  g2.append("rect").attr("x", 0).attr("y", y).attr("width", rectHeight).attr("height", rectHeight).attr("fill", `rgb(${setColor2[0]},${setColor2[1]},${setColor2[2]})`);
153353
- g2.append("text").attr("text-anchor", "start").attr("dominant-baseline", "hanging").attr("x", rectHeight + rectMarginX).attr("y", y).text(setPath.at(-1)).style("font-size", "9px").style("fill", foregroundColor);
153520
+ g2.append("text").attr("text-anchor", "start").attr("dominant-baseline", "hanging").attr("x", rectHeight + rectMarginX).attr("y", y).text(setPath.at(-1) ?? "").style("font-size", "9px").style("fill", foregroundColor);
153354
153521
  y += rectHeight + rectMarginY;
153355
153522
  });
153356
153523
  });
@@ -153371,17 +153538,17 @@ function Legend(props) {
153371
153538
  } else {
153372
153539
  featureSelectionLabelRawStr = featureSelectionLabelRaw == null ? void 0 : featureSelectionLabelRaw[0];
153373
153540
  }
153374
- const combinedMissing = combineMissings(missing, featureAggregationStrategy);
153541
+ const combinedMissing = combineMissings(missing ?? null, featureAggregationStrategy ?? null);
153375
153542
  const featureSelectionLabel = combinedMissing ? `${featureSelectionLabelRawStr} (${Math.round(combinedMissing * 100)}% NaN)` : featureSelectionLabelRawStr;
153376
- const obsLabel = capitalize$2(obsType);
153377
- const featureLabel = considerSelections ? featureSelectionLabel || capitalize$2(featureValueType) : capitalize$2(featureValueType);
153543
+ const obsLabel = capitalize$2(obsType ?? null);
153544
+ const featureLabel = considerSelections ? featureSelectionLabel || capitalize$2(featureValueType ?? null) : capitalize$2(featureValueType ?? null);
153378
153545
  const mainLabel = showObsLabel ? obsLabel : featureLabel;
153379
153546
  const subLabel = showObsLabel ? featureLabel : null;
153380
153547
  const hasSubLabel = subLabel !== null;
153381
153548
  if (!isSetColor) {
153382
- g2.append("text").attr("text-anchor", hasSubLabel ? "start" : "end").attr("dominant-baseline", "hanging").attr("x", hasSubLabel ? 0 : width2).attr("y", 0).text(mainLabel).style("font-size", "10px").style("fill", foregroundColor);
153549
+ g2.append("text").attr("text-anchor", hasSubLabel ? "start" : "end").attr("dominant-baseline", "hanging").attr("x", hasSubLabel ? 0 : width2 - 4).attr("y", 0).text(mainLabel ?? "").style("font-size", "10px").style("fill", foregroundColor);
153383
153550
  if (hasSubLabel) {
153384
- g2.append("text").attr("text-anchor", "end").attr("dominant-baseline", "hanging").attr("x", width2).attr("y", titleHeight).text(subLabel).style("font-size", "9px").style("fill", foregroundColor);
153551
+ g2.append("text").attr("text-anchor", "end").attr("dominant-baseline", "hanging").attr("x", width2).attr("y", titleHeight + rectHeight).text(subLabel ?? "").style("font-size", "9px").style("fill", foregroundColor);
153385
153552
  }
153386
153553
  }
153387
153554
  }, [
@@ -153389,6 +153556,7 @@ function Legend(props) {
153389
153556
  height2,
153390
153557
  featureValueColormap,
153391
153558
  featureValueColormapRange,
153559
+ localRange,
153392
153560
  considerSelections,
153393
153561
  obsType,
153394
153562
  obsColorEncoding,
@@ -153407,20 +153575,63 @@ function Legend(props) {
153407
153575
  contoursFilled,
153408
153576
  contoursVisible,
153409
153577
  pointsVisible,
153410
- featureAggregationStrategy
153578
+ featureAggregationStrategy,
153579
+ setFeatureValueColormapRange,
153580
+ dynamicHeight,
153581
+ highContrast,
153582
+ isStaticColor,
153583
+ missing,
153584
+ showObsLabel,
153585
+ staticColor
153411
153586
  ]);
153412
- return jsxRuntimeExports.jsx("div", { className: clsx$1(classes.legend, {
153587
+ const showInteractiveSlider = setFeatureValueColormapRange && ["geneSelection", "geneExpression"].includes(obsColorEncoding ?? "") && pointsVisible && featureValueColormap;
153588
+ const globalExtent = useMemo(() => {
153589
+ const combined = combineExtents(extent2 ?? null, featureAggregationStrategy ?? null);
153590
+ return combined || [0, 1];
153591
+ }, [extent2, featureAggregationStrategy]);
153592
+ const formatSliderValue = useCallback((value) => {
153593
+ const [xMin, xMax] = globalExtent;
153594
+ const dataValue = xMin + (xMax - xMin) * value;
153595
+ const range2 = xMax - xMin;
153596
+ if (range2 < 0.01) {
153597
+ return dataValue.toExponential(2);
153598
+ }
153599
+ if (range2 < 1) {
153600
+ return dataValue.toFixed(3);
153601
+ }
153602
+ if (range2 < 100) {
153603
+ return dataValue.toFixed(1);
153604
+ }
153605
+ return Math.round(dataValue).toString();
153606
+ }, [globalExtent]);
153607
+ const xlinkHref = featureValueColormap ? getXlinkHref(featureValueColormap) : null;
153608
+ const currentLocalRange = localRange || [0, 1];
153609
+ return jsxRuntimeExports.jsxs("div", { className: clsx$1(classes.legend, {
153413
153610
  [classes.legendRelative]: positionRelative,
153414
153611
  [classes.legendAbsolute]: !positionRelative,
153415
153612
  [classes.legendHighContrast]: highContrast,
153416
153613
  [classes.legendLowContrast]: !highContrast,
153417
153614
  [classes.legendInvisible]: !visible
153418
153615
  }), style: {
153419
- ...needsScroll ? { maxHeight: `${Math.floor(availHeight)}px`, overflowY: "auto" } : { maxHeight: void 0, overflowY: "visible" }
153420
- }, children: jsxRuntimeExports.jsx("svg", { ref: svgRef, style: {
153616
+ ...needsScroll ? { maxHeight: `${Math.floor(availHeight)}px`, overflowY: "auto" } : { maxHeight: void 0, overflowY: "visible" },
153617
+ width: `${width2}px`
153618
+ }, children: [jsxRuntimeExports.jsx("svg", { ref: svgRef, style: {
153421
153619
  width: `${width2}px`,
153422
153620
  height: `${dynamicHeight}px`
153423
- } }) });
153621
+ } }), showInteractiveSlider && xlinkHref && jsxRuntimeExports.jsxs("div", { className: classes.sliderContainer, children: [currentLocalRange[0] > 0 && jsxRuntimeExports.jsx("div", { className: classes.grayTrack, style: {
153622
+ left: 0,
153623
+ width: `${currentLocalRange[0] * 100}%`
153624
+ } }), currentLocalRange[1] < 1 && jsxRuntimeExports.jsx("div", { className: classes.grayTrack, style: {
153625
+ left: `${currentLocalRange[1] * 100}%`,
153626
+ width: `${(1 - currentLocalRange[1]) * 100}%`
153627
+ } }), jsxRuntimeExports.jsx("img", { src: xlinkHref, alt: "Colormap gradient", className: classes.colormapImage, style: {
153628
+ left: `${currentLocalRange[0] * 100}%`,
153629
+ width: `${(currentLocalRange[1] - currentLocalRange[0]) * 100}%`
153630
+ } }), jsxRuntimeExports.jsx(Slider, { className: classes.sliderRoot, value: currentLocalRange, onChange: handleSliderChange, min: 0, max: 1, step: 0.01, disableSwap: true, valueLabelDisplay: "auto", valueLabelFormat: formatSliderValue, "aria-label": "Colormap range", getAriaLabel: (index2) => index2 === 0 ? "Colormap minimum" : "Colormap maximum", getAriaValueText: (value) => formatSliderValue(value), slotProps: {
153631
+ thumb: {
153632
+ className: classes.sliderThumb
153633
+ }
153634
+ } })] })] });
153424
153635
  }
153425
153636
  makeStyles()(() => ({
153426
153637
  multiLegend: {
@@ -153768,7 +153979,8 @@ function EmbeddingScatterplotSubscriber(props) {
153768
153979
  xRange,
153769
153980
  yRange,
153770
153981
  width2,
153771
- height2
153982
+ height2,
153983
+ numCells
153772
153984
  );
153773
153985
  setDynamicCellRadius(pointSizeDevicePixels);
153774
153986
  const nextCellOpacityScale = getPointOpacity(
@@ -154075,6 +154287,7 @@ function EmbeddingScatterplotSubscriber(props) {
154075
154287
  featureLabelsMap,
154076
154288
  featureValueColormap: geneExpressionColormap,
154077
154289
  featureValueColormapRange: geneExpressionColormapRange,
154290
+ setFeatureValueColormapRange: setGeneExpressionColormapRange,
154078
154291
  obsSetSelection: cellSetSelection,
154079
154292
  extent: expressionExtents,
154080
154293
  missing: expressionMissing,
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { D, E } from "./index-DYO3tfis.js";
1
+ import { D, E } from "./index-BgrJUaQb.js";
2
2
  export {
3
3
  D as DualEmbeddingScatterplotSubscriber,
4
4
  E as EmbeddingScatterplotSubscriber
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-DYO3tfis.js";
1
+ import { B as BaseDecoder } from "./index-BgrJUaQb.js";
2
2
  const dctZigZag = new Int32Array([
3
3
  0,
4
4
  1,
@@ -1,5 +1,5 @@
1
1
  import { i as inflate_1 } from "./pako.esm-SxljTded.js";
2
- import { g as getDefaultExportFromCjs, B as BaseDecoder } from "./index-DYO3tfis.js";
2
+ import { g as getDefaultExportFromCjs, B as BaseDecoder } from "./index-BgrJUaQb.js";
3
3
  const LercParameters = {
4
4
  AddCompression: 1
5
5
  };
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-DYO3tfis.js";
1
+ import { B as BaseDecoder } from "./index-BgrJUaQb.js";
2
2
  const MIN_BITS = 9;
3
3
  const CLEAR_CODE = 256;
4
4
  const EOI_CODE = 257;
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-DYO3tfis.js";
1
+ import { B as BaseDecoder } from "./index-BgrJUaQb.js";
2
2
  class PackbitsDecoder extends BaseDecoder {
3
3
  decodeBlock(buffer) {
4
4
  const dataView = new DataView(buffer);
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-DYO3tfis.js";
1
+ import { B as BaseDecoder } from "./index-BgrJUaQb.js";
2
2
  class RawDecoder extends BaseDecoder {
3
3
  decodeBlock(buffer) {
4
4
  return buffer;
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-DYO3tfis.js";
1
+ import { B as BaseDecoder } from "./index-BgrJUaQb.js";
2
2
  class WebImageDecoder extends BaseDecoder {
3
3
  constructor() {
4
4
  super();
@@ -1 +1 @@
1
- {"version":3,"file":"EmbeddingScatterplotSubscriber.d.ts","sourceRoot":"","sources":["../src/EmbeddingScatterplotSubscriber.js"],"names":[],"mappings":"AA6CA;;;;;;;;;;;;GAYG;AACH,sDAVG;IAAsB,IAAI,EAAlB,MAAM;IACQ,KAAK,EAAnB,MAAM;IACQ,kBAAkB,EAAhC,MAAM;IAEU,mBAAmB;IAErB,KAAK,EAAnB,MAAM;IACQ,kBAAkB,EAAhC,MAAM;CAEhB,eA4mBA"}
1
+ {"version":3,"file":"EmbeddingScatterplotSubscriber.d.ts","sourceRoot":"","sources":["../src/EmbeddingScatterplotSubscriber.js"],"names":[],"mappings":"AA6CA;;;;;;;;;;;;GAYG;AACH,sDAVG;IAAsB,IAAI,EAAlB,MAAM;IACQ,KAAK,EAAnB,MAAM;IACQ,kBAAkB,EAAhC,MAAM;IAEU,mBAAmB;IAErB,KAAK,EAAnB,MAAM;IACQ,kBAAkB,EAAhC,MAAM;CAEhB,eA6mBA"}
@@ -148,7 +148,7 @@ export function EmbeddingScatterplotSubscriber(props) {
148
148
  // extents of the cell coordinates on the x/y axes.
149
149
  useEffect(() => {
150
150
  if (xRange && yRange && width && height) {
151
- const pointSizeDevicePixels = getPointSizeDevicePixels(window.devicePixelRatio, zoom, xRange, yRange, width, height);
151
+ const pointSizeDevicePixels = getPointSizeDevicePixels(window.devicePixelRatio, zoom, xRange, yRange, width, height, numCells);
152
152
  setDynamicCellRadius(pointSizeDevicePixels);
153
153
  const nextCellOpacityScale = getPointOpacity(zoom, xRange, yRange, width, height, numCells, averageFillDensity);
154
154
  setDynamicCellOpacity(nextCellOpacityScale);
@@ -326,7 +326,7 @@ export function EmbeddingScatterplotSubscriber(props) {
326
326
  }, updateViewInfo: setComponentViewInfo, getExpressionValue: getExpressionValue, getCellIsSelected: getCellIsSelected, obsSetSelection: cellSetSelection, sampleSetSelection: sampleSetSelection,
327
327
  // InternMap data structures where keys are
328
328
  // obsSet -> sampleSet -> arrayKey -> [].
329
- stratifiedData: stratifiedData, obsSetColor: cellSetColor, sampleSetColor: sampleSetColor, contourThresholds: contourThresholds, contourColorEncoding: contourColorEncoding, contourColor: contourColor, contoursFilled: embeddingContoursFilled, embeddingPointsVisible: embeddingPointsVisible, embeddingContoursVisible: embeddingContoursVisible, circleInfo: circleInfo, featureSelection: geneSelection }), tooltipsVisible && width && height ? (_jsx(ScatterplotTooltipSubscriber, { parentUuid: uuid, obsHighlight: cellHighlight, width: width, height: height, getObsInfo: getObsInfo, featureType: featureType, featureLabelsMap: featureLabelsMap })) : null, _jsx(Legend, { visible: true, theme: theme, featureType: featureType, featureValueType: featureValueType, obsColorEncoding: cellColorEncoding, featureSelection: geneSelection, featureLabelsMap: featureLabelsMap, featureValueColormap: geneExpressionColormap, featureValueColormapRange: geneExpressionColormapRange, obsSetSelection: cellSetSelection, extent: expressionExtents, missing: expressionMissing,
329
+ stratifiedData: stratifiedData, obsSetColor: cellSetColor, sampleSetColor: sampleSetColor, contourThresholds: contourThresholds, contourColorEncoding: contourColorEncoding, contourColor: contourColor, contoursFilled: embeddingContoursFilled, embeddingPointsVisible: embeddingPointsVisible, embeddingContoursVisible: embeddingContoursVisible, circleInfo: circleInfo, featureSelection: geneSelection }), tooltipsVisible && width && height ? (_jsx(ScatterplotTooltipSubscriber, { parentUuid: uuid, obsHighlight: cellHighlight, width: width, height: height, getObsInfo: getObsInfo, featureType: featureType, featureLabelsMap: featureLabelsMap })) : null, _jsx(Legend, { visible: true, theme: theme, featureType: featureType, featureValueType: featureValueType, obsColorEncoding: cellColorEncoding, featureSelection: geneSelection, featureLabelsMap: featureLabelsMap, featureValueColormap: geneExpressionColormap, featureValueColormapRange: geneExpressionColormapRange, setFeatureValueColormapRange: setGeneExpressionColormapRange, obsSetSelection: cellSetSelection, extent: expressionExtents, missing: expressionMissing,
330
330
  // Contour percentile legend
331
331
  pointsVisible: embeddingPointsVisible, contoursVisible: embeddingContoursVisible, contoursFilled: embeddingContoursFilled, contourPercentiles: contourPercentiles || DEFAULT_CONTOUR_PERCENTILES, contourThresholds: contourThresholds, featureAggregationStrategy: featureAggregationStrategyToUse })] }));
332
332
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitessce/scatterplot-embedding",
3
- "version": "3.8.9",
3
+ "version": "3.8.10",
4
4
  "author": "HIDIVE Lab at HMS",
5
5
  "homepage": "http://vitessce.io",
6
6
  "repository": {
@@ -20,13 +20,13 @@
20
20
  "lodash-es": "^4.17.21",
21
21
  "react-aria": "^3.28.0",
22
22
  "@turf/circle": "^7.2.0",
23
- "@vitessce/styles": "3.8.9",
24
- "@vitessce/constants-internal": "3.8.9",
25
- "@vitessce/legend": "3.8.9",
26
- "@vitessce/scatterplot": "3.8.9",
27
- "@vitessce/sets-utils": "3.8.9",
28
- "@vitessce/utils": "3.8.9",
29
- "@vitessce/vit-s": "3.8.9"
23
+ "@vitessce/styles": "3.8.10",
24
+ "@vitessce/constants-internal": "3.8.10",
25
+ "@vitessce/legend": "3.8.10",
26
+ "@vitessce/scatterplot": "3.8.10",
27
+ "@vitessce/sets-utils": "3.8.10",
28
+ "@vitessce/utils": "3.8.10",
29
+ "@vitessce/vit-s": "3.8.10"
30
30
  },
31
31
  "devDependencies": {
32
32
  "react": "18.3.1",
@@ -335,7 +335,7 @@ export function EmbeddingScatterplotSubscriber(props) {
335
335
  useEffect(() => {
336
336
  if (xRange && yRange && width && height) {
337
337
  const pointSizeDevicePixels = getPointSizeDevicePixels(
338
- window.devicePixelRatio, zoom, xRange, yRange, width, height,
338
+ window.devicePixelRatio, zoom, xRange, yRange, width, height, numCells,
339
339
  );
340
340
  setDynamicCellRadius(pointSizeDevicePixels);
341
341
 
@@ -662,6 +662,7 @@ export function EmbeddingScatterplotSubscriber(props) {
662
662
  featureLabelsMap={featureLabelsMap}
663
663
  featureValueColormap={geneExpressionColormap}
664
664
  featureValueColormapRange={geneExpressionColormapRange}
665
+ setFeatureValueColormapRange={setGeneExpressionColormapRange}
665
666
  obsSetSelection={cellSetSelection}
666
667
  extent={expressionExtents}
667
668
  missing={expressionMissing}