@czi-sds/data-viz 2.5.0 → 3.0.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.
@@ -292,17 +292,35 @@ const MIN_WIDTH = 200;
292
292
  */
293
293
  const VIEWER_CONTAINER_NAME = "sds-structure-viewer";
294
294
  /**
295
- * Height reserved for the sequence panel along the bottom, and the viewer
296
- * width above which it grows. Measured against the container above rather than
297
- * the page, so a viewer embedded in a wider page sizes to the room it has.
295
+ * Height reserved for the sequence panel along the bottom when the consumer
296
+ * sets none, and the viewer width above which it grows. Measured against the
297
+ * container above rather than the page, so a viewer embedded in a wider page
298
+ * sizes to the room it has.
298
299
  *
299
- * The legend sits directly on top of the panel and offsets itself by these
300
- * same three values, so it reads them from here rather than restating them.
301
- * Copies that drifted would leave the legend floating off the panel.
300
+ * The legend sits directly on top of the panel and offsets itself by the same
301
+ * heights at the same breakpoint, so it reads them from here rather than
302
+ * restating them. Copies that drifted would leave the legend floating off the
303
+ * panel.
302
304
  */
303
305
  const SEQUENCE_HEIGHT = "max(104px, 30%)";
304
306
  const SEQUENCE_HEIGHT_WIDE = "max(134px, 32%)";
305
307
  /**
308
+ * The sequence panel's height below `WIDE_VIEWER` and at or above it. A height
309
+ * the consumer sets holds at every width, a number is read as pixels, and an
310
+ * empty string leaves the defaults in place, as it would in a React style.
311
+ */
312
+ function sequenceHeights(height) {
313
+ if (height === void 0 || height === "") return {
314
+ base: SEQUENCE_HEIGHT,
315
+ wide: SEQUENCE_HEIGHT_WIDE
316
+ };
317
+ const css = typeof height === "number" ? `${height}px` : height;
318
+ return {
319
+ base: css,
320
+ wide: css
321
+ };
322
+ }
323
+ /**
306
324
  * The element Mol* mounts into.
307
325
  *
308
326
  * This has to be its own node rather than the root: Mol* takes ownership of its
@@ -379,17 +397,20 @@ const ViewerRoot = (0, _emotion_styled.default)("div")`
379
397
  /*
380
398
  * Mol* puts the sequence panel in its "top" region; move it to the bottom and
381
399
  * shrink the main viewport region to match, so the structure sits above the
382
- * sequence rather than behind it. The panel grows on wider viewers, and the
383
- * viewport region's offset follows it.
400
+ * sequence rather than behind it. The panel grows on wider viewers unless the
401
+ * consumer has set its height, and the viewport region's offset follows it.
384
402
  */
385
403
  ${(props) => {
386
- const { showSequenceViewer } = props;
404
+ const { sequenceViewerHeight, showSequenceViewer } = props;
405
+ const height = sequenceHeights(sequenceViewerHeight);
406
+ const mainBottom = showSequenceViewer ? height.base : "0";
407
+ const mainBottomWide = showSequenceViewer ? height.wide : "0";
387
408
  return `
388
409
  .msp-plugin .msp-layout-region.msp-layout-top {
389
410
  display: ${showSequenceViewer ? "block" : "none"} !important;
390
411
  position: absolute !important;
391
412
  inset: auto 0 0 0 !important;
392
- height: ${SEQUENCE_HEIGHT} !important;
413
+ height: ${height.base} !important;
393
414
  }
394
415
 
395
416
  .msp-plugin .msp-layout-region.msp-layout-main {
@@ -397,16 +418,16 @@ const ViewerRoot = (0, _emotion_styled.default)("div")`
397
418
  top: 0 !important;
398
419
  left: 0 !important;
399
420
  right: 0 !important;
400
- bottom: ${showSequenceViewer ? SEQUENCE_HEIGHT : "0"} !important;
421
+ bottom: ${mainBottom} !important;
401
422
  }
402
423
 
403
424
  @container ${VIEWER_CONTAINER_NAME} (min-width: 880px) {
404
425
  .msp-plugin .msp-layout-region.msp-layout-top {
405
- height: ${SEQUENCE_HEIGHT_WIDE} !important;
426
+ height: ${height.wide} !important;
406
427
  }
407
428
 
408
429
  .msp-plugin .msp-layout-region.msp-layout-main {
409
- bottom: ${showSequenceViewer ? SEQUENCE_HEIGHT_WIDE : "0"} !important;
430
+ bottom: ${mainBottomWide} !important;
410
431
  }
411
432
  }
412
433
  `;
@@ -448,12 +469,13 @@ const LegendOverlay = (0, _emotion_styled.default)("div")`
448
469
 
449
470
  ${(props) => {
450
471
  const spaces = (0, _czi_sds_components.getSpaces)(props);
451
- const { showSequenceViewer } = props;
472
+ const { sequenceViewerHeight, showSequenceViewer } = props;
473
+ const height = sequenceHeights(sequenceViewerHeight);
452
474
  return `
453
475
  right: ${spaces === null || spaces === void 0 ? void 0 : spaces.m}px;
454
476
  gap: ${spaces === null || spaces === void 0 ? void 0 : spaces.xs}px;
455
477
  padding-bottom: ${spaces === null || spaces === void 0 ? void 0 : spaces.m}px;
456
- bottom: ${showSequenceViewer ? SEQUENCE_HEIGHT : "0"};
478
+ bottom: ${showSequenceViewer ? height.base : "0"};
457
479
 
458
480
  /*
459
481
  * Track the sequence panel as it grows on wider viewers. The panel is
@@ -461,7 +483,7 @@ const LegendOverlay = (0, _emotion_styled.default)("div")`
461
483
  * would part the legend from it on a viewer narrower than its page.
462
484
  */
463
485
  @container ${VIEWER_CONTAINER_NAME} (min-width: 880px) {
464
- bottom: ${showSequenceViewer ? SEQUENCE_HEIGHT_WIDE : "0"};
486
+ bottom: ${showSequenceViewer ? height.wide : "0"};
465
487
  }
466
488
 
467
489
  /* Spacer balancing the axes widget pinned in the opposite corner. */
@@ -637,7 +659,7 @@ function readoutSlot(label, value, residueCount) {
637
659
  * residue takes priority so the user can inspect others without losing their
638
660
  * selection.
639
661
  */
640
- function StructureLegend({ chainBandColors, chainColors, chains = [], hiddenChains, hoveredResidue = null, onChainHover, onChainSelect, onChainToggle, scale, scaleLabel, scaleMax = null, scaleMin = 0, scaleTooltip, scaleTooltipProps, selectedChains, selectedResidue = null, showSequenceViewer, stats, valueLabel }) {
662
+ function StructureLegend({ chainBandColors, chainColors, chains = [], hiddenChains, hoveredResidue = null, onChainHover, onChainSelect, onChainToggle, scale, scaleLabel, scaleMax = null, scaleMin = 0, scaleTooltip, scaleTooltipProps, selectedChains, selectedResidue = null, sequenceViewerHeight, showSequenceViewer, stats, valueLabel }) {
641
663
  var _activeResidue$residu, _stats$, _stats$2, _stats$3;
642
664
  const activeResidue = hoveredResidue !== null && hoveredResidue !== void 0 ? hoveredResidue : selectedResidue;
643
665
  const showResidue = activeResidue !== null;
@@ -652,6 +674,7 @@ function StructureLegend({ chainBandColors, chainColors, chains = [], hiddenChai
652
674
  showResidue ? readoutSlot("pLDDT", activeResidue.plddt, residueCount) : (_stats$3 = stats[2]) !== null && _stats$3 !== void 0 ? _stats$3 : null
653
675
  ];
654
676
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(LegendOverlay, {
677
+ sequenceViewerHeight,
655
678
  showSequenceViewer,
656
679
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsGrid, { children: Array.from({ length: SLOT_COUNT }, (_, i) => {
657
680
  var _slots$i;
@@ -3638,6 +3661,7 @@ const _excluded = [
3638
3661
  "sceneMode",
3639
3662
  "selection",
3640
3663
  "sequenceViewerBackgroundColor",
3664
+ "sequenceViewerHeight",
3641
3665
  "showAxes",
3642
3666
  "showChainLegend",
3643
3667
  "showLegend",
@@ -3711,7 +3735,7 @@ function resolveScaleProps(colorBy, overlay) {
3711
3735
  */
3712
3736
  const ProteinStructureViewer = (0, react.forwardRef)((props, ref) => {
3713
3737
  var _selection$chains;
3714
- const { backgroundColor, chainColors: chainColorOverrides, colorBy: colorByProp, disableChainHighlightOnHover = false, download, hiddenChains: hiddenChainsProp, highlights, initialCamera, onCameraChange, onChainVisibilityChange, onChainsChange, molstarSpec, onDispose, onError, onReady, onResidueClick, onResidueHover, onSelectionChange, onStructureLoad, orientation, projection, representation = "cartoon", structure, plddt, residueOverlay, sceneMode = "managed", selection: selectionProp, sequenceViewerBackgroundColor, showAxes = true, showChainLegend = true, showLegend = true, showSequenceViewer = true, stats } = props, rest = require_objectSpread2._objectWithoutProperties(props, _excluded);
3738
+ const { backgroundColor, chainColors: chainColorOverrides, colorBy: colorByProp, disableChainHighlightOnHover = false, download, hiddenChains: hiddenChainsProp, highlights, initialCamera, onCameraChange, onChainVisibilityChange, onChainsChange, molstarSpec, onDispose, onError, onReady, onResidueClick, onResidueHover, onSelectionChange, onStructureLoad, orientation, projection, representation = "cartoon", structure, plddt, residueOverlay, sceneMode = "managed", selection: selectionProp, sequenceViewerBackgroundColor, sequenceViewerHeight, showAxes = true, showChainLegend = true, showLegend = true, showSequenceViewer = true, stats } = props, rest = require_objectSpread2._objectWithoutProperties(props, _excluded);
3715
3739
  const theme = (0, _mui_material_styles.useTheme)();
3716
3740
  const mode = (0, _czi_sds_components.getMode)({ theme });
3717
3741
  const semanticColors = (0, _czi_sds_components.getSemanticColors)({ theme });
@@ -3892,6 +3916,7 @@ const ProteinStructureViewer = (0, react.forwardRef)((props, ref) => {
3892
3916
  const plddtColoringActive = colorBy === "plddt";
3893
3917
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ViewerRoot, require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({
3894
3918
  ref,
3919
+ sequenceViewerHeight,
3895
3920
  showSequenceViewer
3896
3921
  }, rest), {}, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginMount, { ref: pluginMountRef }), showLegend && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StructureLegend, require_objectSpread2._objectSpread2({
3897
3922
  chainBandColors: plddtColoringActive ? require_renderStructureImage.PLDDT_BAND_COLORS : void 0,
@@ -3904,6 +3929,7 @@ const ProteinStructureViewer = (0, react.forwardRef)((props, ref) => {
3904
3929
  onChainToggle: toggleChain,
3905
3930
  selectedChains,
3906
3931
  selectedResidue: selectedReadout,
3932
+ sequenceViewerHeight,
3907
3933
  showSequenceViewer,
3908
3934
  stats: stats !== null && stats !== void 0 ? stats : []
3909
3935
  }, scaleProps))] }));
@@ -290,17 +290,35 @@ const MIN_WIDTH = 200;
290
290
  */
291
291
  const VIEWER_CONTAINER_NAME = "sds-structure-viewer";
292
292
  /**
293
- * Height reserved for the sequence panel along the bottom, and the viewer
294
- * width above which it grows. Measured against the container above rather than
295
- * the page, so a viewer embedded in a wider page sizes to the room it has.
293
+ * Height reserved for the sequence panel along the bottom when the consumer
294
+ * sets none, and the viewer width above which it grows. Measured against the
295
+ * container above rather than the page, so a viewer embedded in a wider page
296
+ * sizes to the room it has.
296
297
  *
297
- * The legend sits directly on top of the panel and offsets itself by these
298
- * same three values, so it reads them from here rather than restating them.
299
- * Copies that drifted would leave the legend floating off the panel.
298
+ * The legend sits directly on top of the panel and offsets itself by the same
299
+ * heights at the same breakpoint, so it reads them from here rather than
300
+ * restating them. Copies that drifted would leave the legend floating off the
301
+ * panel.
300
302
  */
301
303
  const SEQUENCE_HEIGHT = "max(104px, 30%)";
302
304
  const SEQUENCE_HEIGHT_WIDE = "max(134px, 32%)";
303
305
  /**
306
+ * The sequence panel's height below `WIDE_VIEWER` and at or above it. A height
307
+ * the consumer sets holds at every width, a number is read as pixels, and an
308
+ * empty string leaves the defaults in place, as it would in a React style.
309
+ */
310
+ function sequenceHeights(height) {
311
+ if (height === void 0 || height === "") return {
312
+ base: SEQUENCE_HEIGHT,
313
+ wide: SEQUENCE_HEIGHT_WIDE
314
+ };
315
+ const css = typeof height === "number" ? `${height}px` : height;
316
+ return {
317
+ base: css,
318
+ wide: css
319
+ };
320
+ }
321
+ /**
304
322
  * The element Mol* mounts into.
305
323
  *
306
324
  * This has to be its own node rather than the root: Mol* takes ownership of its
@@ -377,17 +395,20 @@ const ViewerRoot = styled("div")`
377
395
  /*
378
396
  * Mol* puts the sequence panel in its "top" region; move it to the bottom and
379
397
  * shrink the main viewport region to match, so the structure sits above the
380
- * sequence rather than behind it. The panel grows on wider viewers, and the
381
- * viewport region's offset follows it.
398
+ * sequence rather than behind it. The panel grows on wider viewers unless the
399
+ * consumer has set its height, and the viewport region's offset follows it.
382
400
  */
383
401
  ${(props) => {
384
- const { showSequenceViewer } = props;
402
+ const { sequenceViewerHeight, showSequenceViewer } = props;
403
+ const height = sequenceHeights(sequenceViewerHeight);
404
+ const mainBottom = showSequenceViewer ? height.base : "0";
405
+ const mainBottomWide = showSequenceViewer ? height.wide : "0";
385
406
  return `
386
407
  .msp-plugin .msp-layout-region.msp-layout-top {
387
408
  display: ${showSequenceViewer ? "block" : "none"} !important;
388
409
  position: absolute !important;
389
410
  inset: auto 0 0 0 !important;
390
- height: ${SEQUENCE_HEIGHT} !important;
411
+ height: ${height.base} !important;
391
412
  }
392
413
 
393
414
  .msp-plugin .msp-layout-region.msp-layout-main {
@@ -395,16 +416,16 @@ const ViewerRoot = styled("div")`
395
416
  top: 0 !important;
396
417
  left: 0 !important;
397
418
  right: 0 !important;
398
- bottom: ${showSequenceViewer ? SEQUENCE_HEIGHT : "0"} !important;
419
+ bottom: ${mainBottom} !important;
399
420
  }
400
421
 
401
422
  @container ${VIEWER_CONTAINER_NAME} (min-width: 880px) {
402
423
  .msp-plugin .msp-layout-region.msp-layout-top {
403
- height: ${SEQUENCE_HEIGHT_WIDE} !important;
424
+ height: ${height.wide} !important;
404
425
  }
405
426
 
406
427
  .msp-plugin .msp-layout-region.msp-layout-main {
407
- bottom: ${showSequenceViewer ? SEQUENCE_HEIGHT_WIDE : "0"} !important;
428
+ bottom: ${mainBottomWide} !important;
408
429
  }
409
430
  }
410
431
  `;
@@ -446,12 +467,13 @@ const LegendOverlay = styled("div")`
446
467
 
447
468
  ${(props) => {
448
469
  const spaces = getSpaces(props);
449
- const { showSequenceViewer } = props;
470
+ const { sequenceViewerHeight, showSequenceViewer } = props;
471
+ const height = sequenceHeights(sequenceViewerHeight);
450
472
  return `
451
473
  right: ${spaces === null || spaces === void 0 ? void 0 : spaces.m}px;
452
474
  gap: ${spaces === null || spaces === void 0 ? void 0 : spaces.xs}px;
453
475
  padding-bottom: ${spaces === null || spaces === void 0 ? void 0 : spaces.m}px;
454
- bottom: ${showSequenceViewer ? SEQUENCE_HEIGHT : "0"};
476
+ bottom: ${showSequenceViewer ? height.base : "0"};
455
477
 
456
478
  /*
457
479
  * Track the sequence panel as it grows on wider viewers. The panel is
@@ -459,7 +481,7 @@ const LegendOverlay = styled("div")`
459
481
  * would part the legend from it on a viewer narrower than its page.
460
482
  */
461
483
  @container ${VIEWER_CONTAINER_NAME} (min-width: 880px) {
462
- bottom: ${showSequenceViewer ? SEQUENCE_HEIGHT_WIDE : "0"};
484
+ bottom: ${showSequenceViewer ? height.wide : "0"};
463
485
  }
464
486
 
465
487
  /* Spacer balancing the axes widget pinned in the opposite corner. */
@@ -635,7 +657,7 @@ function readoutSlot(label, value, residueCount) {
635
657
  * residue takes priority so the user can inspect others without losing their
636
658
  * selection.
637
659
  */
638
- function StructureLegend({ chainBandColors, chainColors, chains = [], hiddenChains, hoveredResidue = null, onChainHover, onChainSelect, onChainToggle, scale, scaleLabel, scaleMax = null, scaleMin = 0, scaleTooltip, scaleTooltipProps, selectedChains, selectedResidue = null, showSequenceViewer, stats, valueLabel }) {
660
+ function StructureLegend({ chainBandColors, chainColors, chains = [], hiddenChains, hoveredResidue = null, onChainHover, onChainSelect, onChainToggle, scale, scaleLabel, scaleMax = null, scaleMin = 0, scaleTooltip, scaleTooltipProps, selectedChains, selectedResidue = null, sequenceViewerHeight, showSequenceViewer, stats, valueLabel }) {
639
661
  var _activeResidue$residu, _stats$, _stats$2, _stats$3;
640
662
  const activeResidue = hoveredResidue !== null && hoveredResidue !== void 0 ? hoveredResidue : selectedResidue;
641
663
  const showResidue = activeResidue !== null;
@@ -650,6 +672,7 @@ function StructureLegend({ chainBandColors, chainColors, chains = [], hiddenChai
650
672
  showResidue ? readoutSlot("pLDDT", activeResidue.plddt, residueCount) : (_stats$3 = stats[2]) !== null && _stats$3 !== void 0 ? _stats$3 : null
651
673
  ];
652
674
  return /* @__PURE__ */ jsxs(LegendOverlay, {
675
+ sequenceViewerHeight,
653
676
  showSequenceViewer,
654
677
  children: [/* @__PURE__ */ jsx(StatsGrid, { children: Array.from({ length: SLOT_COUNT }, (_, i) => {
655
678
  var _slots$i;
@@ -3636,6 +3659,7 @@ const _excluded = [
3636
3659
  "sceneMode",
3637
3660
  "selection",
3638
3661
  "sequenceViewerBackgroundColor",
3662
+ "sequenceViewerHeight",
3639
3663
  "showAxes",
3640
3664
  "showChainLegend",
3641
3665
  "showLegend",
@@ -3698,7 +3722,7 @@ function resolveScaleProps(colorBy, overlay) {
3698
3722
  }
3699
3723
  var ProteinStructureViewer_default = memo(forwardRef((props, ref) => {
3700
3724
  var _selection$chains;
3701
- const { backgroundColor, chainColors: chainColorOverrides, colorBy: colorByProp, disableChainHighlightOnHover = false, download, hiddenChains: hiddenChainsProp, highlights, initialCamera, onCameraChange, onChainVisibilityChange, onChainsChange, molstarSpec, onDispose, onError, onReady, onResidueClick, onResidueHover, onSelectionChange, onStructureLoad, orientation, projection, representation = "cartoon", structure, plddt, residueOverlay, sceneMode = "managed", selection: selectionProp, sequenceViewerBackgroundColor, showAxes = true, showChainLegend = true, showLegend = true, showSequenceViewer = true, stats } = props, rest = _objectWithoutProperties(props, _excluded);
3725
+ const { backgroundColor, chainColors: chainColorOverrides, colorBy: colorByProp, disableChainHighlightOnHover = false, download, hiddenChains: hiddenChainsProp, highlights, initialCamera, onCameraChange, onChainVisibilityChange, onChainsChange, molstarSpec, onDispose, onError, onReady, onResidueClick, onResidueHover, onSelectionChange, onStructureLoad, orientation, projection, representation = "cartoon", structure, plddt, residueOverlay, sceneMode = "managed", selection: selectionProp, sequenceViewerBackgroundColor, sequenceViewerHeight, showAxes = true, showChainLegend = true, showLegend = true, showSequenceViewer = true, stats } = props, rest = _objectWithoutProperties(props, _excluded);
3702
3726
  const theme = useTheme();
3703
3727
  const mode = getMode({ theme });
3704
3728
  const semanticColors = getSemanticColors({ theme });
@@ -3879,6 +3903,7 @@ var ProteinStructureViewer_default = memo(forwardRef((props, ref) => {
3879
3903
  const plddtColoringActive = colorBy === "plddt";
3880
3904
  return /* @__PURE__ */ jsxs(ViewerRoot, _objectSpread2(_objectSpread2({
3881
3905
  ref,
3906
+ sequenceViewerHeight,
3882
3907
  showSequenceViewer
3883
3908
  }, rest), {}, { children: [/* @__PURE__ */ jsx(PluginMount, { ref: pluginMountRef }), showLegend && /* @__PURE__ */ jsx(StructureLegend, _objectSpread2({
3884
3909
  chainBandColors: plddtColoringActive ? PLDDT_BAND_COLORS : void 0,
@@ -3891,6 +3916,7 @@ var ProteinStructureViewer_default = memo(forwardRef((props, ref) => {
3891
3916
  onChainToggle: toggleChain,
3892
3917
  selectedChains,
3893
3918
  selectedResidue: selectedReadout,
3919
+ sequenceViewerHeight,
3894
3920
  showSequenceViewer,
3895
3921
  stats: stats !== null && stats !== void 0 ? stats : []
3896
3922
  }, scaleProps))] }));
@@ -21,12 +21,12 @@ const TitleContainer = (0, _emotion_styled.default)("div")`
21
21
  const spaces = (0, _czi_sds_components.getSpaces)(props);
22
22
  return `
23
23
  gap: ${spaces === null || spaces === void 0 ? void 0 : spaces.s}px;
24
- margin-bottom: ${spaces === null || spaces === void 0 ? void 0 : spaces.l}px;
24
+ margin-bottom: ${spaces === null || spaces === void 0 ? void 0 : spaces.s}px;
25
25
  `;
26
26
  }}
27
27
  `;
28
28
  const ChartTitle = (0, _emotion_styled.default)("div")`
29
- ${_czi_sds_components.fontHeaderS}
29
+ ${_czi_sds_components.fontBodyMediumXs}
30
30
 
31
31
  ${(props) => {
32
32
  var _semanticColors$base;
@@ -36,36 +36,19 @@ const ChartTitle = (0, _emotion_styled.default)("div")`
36
36
  `;
37
37
  }}
38
38
  `;
39
- const StyledBadge = (0, _emotion_styled.default)("div")`
40
- ${_czi_sds_components.fontTabularSemiboldXxxxs}
41
-
42
- ${(props) => {
43
- var _semanticColors$base2, _semanticColors$base3;
44
- const spaces = (0, _czi_sds_components.getSpaces)(props);
45
- const corners = (0, _czi_sds_components.getCorners)(props);
46
- const semanticColors = (0, _czi_sds_components.getSemanticColors)(props);
47
- return `
48
- padding: ${spaces === null || spaces === void 0 ? void 0 : spaces.xxxs}px ${spaces === null || spaces === void 0 ? void 0 : spaces.xxs}px;
49
- border-radius: ${corners === null || corners === void 0 ? void 0 : corners.m}px;
50
- background-color: ${semanticColors === null || semanticColors === void 0 || (_semanticColors$base2 = semanticColors.base) === null || _semanticColors$base2 === void 0 ? void 0 : _semanticColors$base2.backgroundTertiary};
51
- color: ${semanticColors === null || semanticColors === void 0 || (_semanticColors$base3 = semanticColors.base) === null || _semanticColors$base3 === void 0 ? void 0 : _semanticColors$base3.textSecondary};
52
- `;
53
- }}
54
- `;
55
39
  const BarContainer = (0, _emotion_styled.default)("div")`
56
40
  ${(props) => {
57
- var _semanticColors$base4;
41
+ var _semanticColors$base2;
58
42
  const { width, barHeight, isEmpty } = props;
59
43
  const corners = (0, _czi_sds_components.getCorners)(props);
60
44
  const semanticColors = (0, _czi_sds_components.getSemanticColors)(props);
61
45
  const finalWidth = typeof width === "number" ? `${width}px` : width;
62
46
  return `
63
47
  display: flex;
64
- border-radius: ${corners === null || corners === void 0 ? void 0 : corners.s}px;
48
+ border-radius: ${corners === null || corners === void 0 ? void 0 : corners.m}px;
65
49
  width: ${finalWidth};
66
50
  height: ${barHeight}px;
67
- background-color: ${isEmpty ? semanticColors === null || semanticColors === void 0 || (_semanticColors$base4 = semanticColors.base) === null || _semanticColors$base4 === void 0 ? void 0 : _semanticColors$base4.backgroundTertiary : "transparent"};
68
- overflow: hidden;
51
+ background-color: ${isEmpty ? semanticColors === null || semanticColors === void 0 || (_semanticColors$base2 = semanticColors.base) === null || _semanticColors$base2 === void 0 ? void 0 : _semanticColors$base2.backgroundTertiary : "transparent"};
69
52
  `;
70
53
  }}
71
54
  `;
@@ -73,14 +56,17 @@ const BarSegment = (0, _emotion_styled.default)("div")`
73
56
  ${(props) => {
74
57
  const { color, height, percentage, opacity, disabled, isLast } = props;
75
58
  const spaces = (0, _czi_sds_components.getSpaces)(props);
59
+ const corners = (0, _czi_sds_components.getCorners)(props);
76
60
  const gap = (spaces === null || spaces === void 0 ? void 0 : spaces.xxxs) || 0;
61
+ const marginRight = percentage > 0 && !isLast ? `${gap}px` : 0;
77
62
  return `
78
63
  background-color: ${color};
64
+ border-radius: ${corners === null || corners === void 0 ? void 0 : corners.m}px;
79
65
  height: ${height}px;
80
66
  flex-grow: ${percentage};
81
67
  flex-basis: 0;
82
68
  flex-shrink: 1;
83
- margin-right: ${percentage > 0 && !isLast ? `${gap}px` : 0};
69
+ margin-right: ${marginRight};
84
70
  opacity: ${opacity};
85
71
  transition: opacity 0.2s ease-in-out, flex-grow 0.25s ease-in-out, margin-right 0.25s ease-out;
86
72
  cursor: ${disabled ? "default" : "pointer"};
@@ -94,7 +80,7 @@ const StyledStackedBarChartWrapper = (0, _emotion_styled.default)("div")`
94
80
  return `
95
81
  display: flex;
96
82
  flex-direction: column;
97
- gap: ${spaces === null || spaces === void 0 ? void 0 : spaces.m}px;
83
+ gap: ${spaces === null || spaces === void 0 ? void 0 : spaces.s}px;
98
84
  `;
99
85
  }}
100
86
  `;
@@ -124,17 +110,18 @@ const _excluded = [
124
110
  "remainingLabel",
125
111
  "remainingUnit",
126
112
  "unit",
127
- "colorGeneratorOptions"
113
+ "colorGeneratorOptions",
114
+ "maxItems"
128
115
  ];
129
116
  const calculateBadgeText = (totalItems, selectedCount) => {
130
117
  if (selectedCount === 0 || selectedCount === totalItems) return `${totalItems}`;
131
118
  return `${selectedCount} of ${totalItems}`;
132
119
  };
133
120
  const calculateLegendValue = (item, showLegendValues, legendValueFormat, globalUnit) => {
121
+ var _ref, _item$unit;
134
122
  if (!showLegendValues) return void 0;
135
123
  if (legendValueFormat === "percentage") return `${Math.round(item.percentage)}%`;
136
- const effectiveUnit = item.unit || globalUnit;
137
- return effectiveUnit ? `${item.value} ${effectiveUnit}` : `${item.value}`;
124
+ return `${item.value.toLocaleString()} ${(_ref = (_item$unit = item.unit) !== null && _item$unit !== void 0 ? _item$unit : globalUnit) !== null && _ref !== void 0 ? _ref : ""}`.trim();
138
125
  };
139
126
  const getSegmentOpacity = (index, hoveredIndex, selectedIndicesSet, selectionBehavior) => {
140
127
  const hasSelection = selectedIndicesSet.size > 0;
@@ -181,7 +168,7 @@ const applySelectionStyling = (items, selectedIndicesSet, selectionBehavior, mod
181
168
  return mode === "cumulative" ? applyCumulativeHideStyling(items, selectedIndicesSet, selectedTotal, hasRemaining, effectiveMaxAmount) : applyProportionalHideStyling(items, selectedIndicesSet, selectedTotal);
182
169
  };
183
170
  const buildLegendItems = (dataWithPercentages, options) => {
184
- const { showLegendValues, legendValueFormat, unit, hasRemaining, showLegend, remainingValue, remainingPercentage, remainingLabel, remainingUnit, theme } = options;
171
+ const { showLegendValues, legendValueFormat, unit, hasRemaining, showLegend, remainingValue, remainingPercentage, remainingLabel, remainingUnit, remainingOpensMenu, theme } = options;
185
172
  const legendItems = dataWithPercentages.map((item) => ({
186
173
  name: item.name,
187
174
  value: calculateLegendValue(item, showLegendValues, legendValueFormat, unit),
@@ -195,13 +182,13 @@ const buildLegendItems = (dataWithPercentages, options) => {
195
182
  else if (legendValueFormat === "percentage") remainingValueDisplay = `${Math.round(remainingPercentage)}%`;
196
183
  else {
197
184
  const effectiveRemainingUnit = remainingUnit || unit || "";
198
- remainingValueDisplay = effectiveRemainingUnit ? `${remainingValue} ${effectiveRemainingUnit}` : `${remainingValue}`;
185
+ remainingValueDisplay = effectiveRemainingUnit ? `${remainingValue.toLocaleString()} ${effectiveRemainingUnit}` : `${remainingValue.toLocaleString()}`;
199
186
  }
200
187
  legendItems.push({
201
188
  name: remainingLabel,
202
189
  value: remainingValueDisplay,
203
190
  color: theme === null || theme === void 0 || (_theme$palette = theme.palette) === null || _theme$palette === void 0 || (_theme$palette = _theme$palette.sds) === null || _theme$palette === void 0 || (_theme$palette = _theme$palette.base) === null || _theme$palette === void 0 ? void 0 : _theme$palette.backgroundTertiary,
204
- disabled: true
191
+ disabled: !remainingOpensMenu
205
192
  });
206
193
  }
207
194
  return legendItems;
@@ -237,10 +224,13 @@ const renderBarSegment = (item, options) => {
237
224
  }, item.key) : (0, react.cloneElement)(barSegment, { key: item.key });
238
225
  };
239
226
  const StackedBarChart = (props) => {
240
- var _theme$palette4;
241
- const { title, badge, hideBadge = false, data, width = "100%", barHeight: barHeightProp = 16, showLegend = true, showLegendValues = true, legendValueFormat = "percentage", selectedIndices = [], onSelectionChange, onSegmentMouseEnter, onSegmentMouseLeave, onLegendItemMouseEnter, onLegendItemMouseLeave, onSegmentClick, onLegendItemClick, selectionBehavior = "dim", mode = "proportional", maxAmount, remainingLabel = "Remaining", remainingUnit, unit, colorGeneratorOptions } = props, rest = require_objectSpread2._objectWithoutProperties(props, _excluded);
227
+ var _theme$palette5;
228
+ const { title, badge, hideBadge = false, data, width = "100%", barHeight: barHeightProp = 8, showLegend = true, showLegendValues = true, legendValueFormat = "percentage", selectedIndices = [], onSelectionChange, onSegmentMouseEnter, onSegmentMouseLeave, onLegendItemMouseEnter, onLegendItemMouseLeave, onSegmentClick, onLegendItemClick, selectionBehavior = "dim", mode = "proportional", maxAmount, remainingLabel = "Remaining", remainingUnit, unit, colorGeneratorOptions, maxItems } = props, rest = require_objectSpread2._objectWithoutProperties(props, _excluded);
242
229
  const barHeight = Math.max(1, barHeightProp);
243
230
  const theme = (0, _mui_material.useTheme)();
231
+ const visibleCount = mode === "cumulative" && maxItems !== void 0 && maxItems < data.length ? Math.max(0, maxItems) : data.length;
232
+ const chartData = (0, react.useMemo)(() => data.slice(0, visibleCount), [data, visibleCount]);
233
+ const hiddenItems = (0, react.useMemo)(() => data.slice(visibleCount), [data, visibleCount]);
244
234
  const [hoveredIndex, setHoveredIndex] = (0, react.useState)(null);
245
235
  const [animatedItems, setAnimatedItems] = (0, react.useState)([]);
246
236
  const [autoColorMap, setAutoColorMap] = (0, react.useState)(/* @__PURE__ */ new Map());
@@ -263,8 +253,8 @@ const StackedBarChart = (props) => {
263
253
  theme
264
254
  ]);
265
255
  (0, react.useEffect)(() => {
266
- const autoItems = data.filter((item) => !item.color);
267
- const currentDataKeys = new Set(data.map((item) => item.name));
256
+ const autoItems = chartData.filter((item) => !item.color);
257
+ const currentDataKeys = new Set(chartData.map((item) => item.name));
268
258
  const previousDataKeys = previousDataKeysRef.current;
269
259
  const previousColorOptions = previousColorOptionsRef.current;
270
260
  const colorOptionsChanged = !previousColorOptions || previousColorOptions.isDarkMode !== isDarkMode || previousColorOptions.options !== colorGeneratorOptions;
@@ -290,7 +280,7 @@ const StackedBarChart = (props) => {
290
280
  if (hasChanges) setAutoColorMap(updatedMap);
291
281
  }
292
282
  }, [
293
- data,
283
+ chartData,
294
284
  colorGeneratorOptions,
295
285
  isDarkMode,
296
286
  theme,
@@ -299,9 +289,10 @@ const StackedBarChart = (props) => {
299
289
  const displayBadge = badge !== null && badge !== void 0 ? badge : calculateBadgeText(data.length, selectedIndices.length);
300
290
  const { effectiveMaxAmount, hasRemaining, remainingValue, remainingPercentage } = (0, react.useMemo)(() => {
301
291
  const total = data.reduce((sum, item) => sum + item.value, 0);
292
+ const visibleTotal = chartData.reduce((sum, item) => sum + item.value, 0);
302
293
  const maxAmountValue = mode === "cumulative" && maxAmount ? maxAmount : total;
303
- const hasRemainingSegment = mode === "cumulative" && total < maxAmountValue;
304
- const remaining = hasRemainingSegment ? maxAmountValue - total : 0;
294
+ const hasRemainingSegment = mode === "cumulative" && visibleTotal < maxAmountValue;
295
+ const remaining = hasRemainingSegment ? maxAmountValue - visibleTotal : 0;
305
296
  return {
306
297
  effectiveMaxAmount: maxAmountValue,
307
298
  hasRemaining: hasRemainingSegment,
@@ -310,11 +301,20 @@ const StackedBarChart = (props) => {
310
301
  };
311
302
  }, [
312
303
  data,
304
+ chartData,
313
305
  mode,
314
306
  maxAmount
315
307
  ]);
308
+ const hasOverflow = hasRemaining && hiddenItems.length > 0;
309
+ const overflowIndex = data.length;
310
+ const hiddenColors = (0, react.useMemo)(() => hasOverflow ? (0, _czi_sds_components.generateDiscreteColors)(hiddenItems.length, require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, colorGeneratorOptions || {}), {}, { isDarkMode })) : [], [
311
+ hasOverflow,
312
+ hiddenItems.length,
313
+ colorGeneratorOptions,
314
+ isDarkMode
315
+ ]);
316
316
  const dataWithPercentages = (0, react.useMemo)(() => {
317
- return data.map((item, index) => {
317
+ return chartData.map((item, index) => {
318
318
  var _theme$palette3;
319
319
  const resolvedColor = item.color || autoColorMap.get(item.name) || (theme === null || theme === void 0 || (_theme$palette3 = theme.palette) === null || _theme$palette3 === void 0 || (_theme$palette3 = _theme$palette3.sds) === null || _theme$palette3 === void 0 || (_theme$palette3 = _theme$palette3.base) === null || _theme$palette3 === void 0 ? void 0 : _theme$palette3.ornamentSecondary);
320
320
  return require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, item), {}, {
@@ -327,7 +327,7 @@ const StackedBarChart = (props) => {
327
327
  });
328
328
  });
329
329
  }, [
330
- data,
330
+ chartData,
331
331
  effectiveMaxAmount,
332
332
  autoColorMap,
333
333
  theme
@@ -368,10 +368,16 @@ const StackedBarChart = (props) => {
368
368
  timeoutIds.forEach((id) => window.clearTimeout(id));
369
369
  };
370
370
  }, [dataWithPercentages, remainingPercentage]);
371
- const selectedIndicesSet = (0, react.useMemo)(() => new Set(selectedIndices), [selectedIndices]);
372
- const { styledItems, updatedRemainingPercentage, updatedRemainingValue } = (0, react.useMemo)(() => applySelectionStyling(animatedItems, selectedIndicesSet, selectionBehavior, mode, hasRemaining, effectiveMaxAmount, animatedRemainingPercentage, remainingValue), [
371
+ const toChartIndex = (0, react.useCallback)((index) => hasOverflow && index >= visibleCount ? overflowIndex : index, [
372
+ hasOverflow,
373
+ visibleCount,
374
+ overflowIndex
375
+ ]);
376
+ const hoveredChartIndex = hoveredIndex === null ? null : toChartIndex(hoveredIndex);
377
+ const selectedChartIndices = (0, react.useMemo)(() => new Set(selectedIndices.map(toChartIndex)), [selectedIndices, toChartIndex]);
378
+ const { styledItems, updatedRemainingPercentage, updatedRemainingValue } = (0, react.useMemo)(() => applySelectionStyling(animatedItems, selectedChartIndices, selectionBehavior, mode, hasRemaining, effectiveMaxAmount, animatedRemainingPercentage, remainingValue), [
373
379
  animatedItems,
374
- selectedIndicesSet,
380
+ selectedChartIndices,
375
381
  selectionBehavior,
376
382
  mode,
377
383
  hasRemaining,
@@ -392,6 +398,7 @@ const StackedBarChart = (props) => {
392
398
  remainingPercentage: displayRemainingPercentage,
393
399
  remainingLabel,
394
400
  remainingUnit,
401
+ remainingOpensMenu: hasOverflow,
395
402
  theme
396
403
  }), [
397
404
  dataWithPercentages,
@@ -404,19 +411,51 @@ const StackedBarChart = (props) => {
404
411
  displayRemainingPercentage,
405
412
  remainingLabel,
406
413
  remainingUnit,
414
+ hasOverflow,
407
415
  theme
408
416
  ]);
409
- const handleLegendItemHover = (0, react.useCallback)((_item, index) => {
417
+ const toLegendIndex = (0, react.useCallback)((index) => {
418
+ if (!hasOverflow || index < visibleCount) return index;
419
+ return index === overflowIndex ? visibleCount : index + 1;
420
+ }, [
421
+ hasOverflow,
422
+ visibleCount,
423
+ overflowIndex
424
+ ]);
425
+ const fromLegendIndex = (0, react.useCallback)((index) => {
426
+ if (!hasOverflow || index < visibleCount) return index;
427
+ return index === visibleCount ? overflowIndex : index - 1;
428
+ }, [
429
+ hasOverflow,
430
+ visibleCount,
431
+ overflowIndex
432
+ ]);
433
+ const handleLegendItemHover = (0, react.useCallback)((_item, legendIndex) => {
434
+ const index = fromLegendIndex(legendIndex);
410
435
  setHoveredIndex(index);
411
436
  if (onLegendItemMouseEnter && data[index]) onLegendItemMouseEnter(data[index], index);
412
- }, [onLegendItemMouseEnter, data]);
413
- const handleLegendItemLeave = (0, react.useCallback)((_item, index) => {
437
+ }, [
438
+ onLegendItemMouseEnter,
439
+ data,
440
+ fromLegendIndex
441
+ ]);
442
+ const handleLegendItemLeave = (0, react.useCallback)((_item, legendIndex) => {
443
+ const index = fromLegendIndex(legendIndex);
414
444
  setHoveredIndex(null);
415
445
  if (onLegendItemMouseLeave && data[index]) onLegendItemMouseLeave(data[index], index);
416
- }, [onLegendItemMouseLeave, data]);
417
- const handleLegendItemClickInternal = (0, react.useCallback)((_item, index) => {
446
+ }, [
447
+ onLegendItemMouseLeave,
448
+ data,
449
+ fromLegendIndex
450
+ ]);
451
+ const handleLegendItemClickInternal = (0, react.useCallback)((_item, legendIndex) => {
452
+ const index = fromLegendIndex(legendIndex);
418
453
  if (onLegendItemClick && data[index]) onLegendItemClick(data[index], index);
419
- }, [onLegendItemClick, data]);
454
+ }, [
455
+ onLegendItemClick,
456
+ data,
457
+ fromLegendIndex
458
+ ]);
420
459
  const handleSelectionChange = (0, react.useCallback)((newSelectedIndices) => {
421
460
  if (!onSelectionChange) return;
422
461
  onSelectionChange(newSelectedIndices, data.filter((item, itemIndex) => newSelectedIndices.includes(itemIndex)));
@@ -424,15 +463,29 @@ const StackedBarChart = (props) => {
424
463
  const handleSegmentClick = (0, react.useCallback)((index) => {
425
464
  if (onSegmentClick && data[index]) onSegmentClick(data[index], index);
426
465
  if (!onSelectionChange) return;
427
- handleSelectionChange(selectedIndicesSet.has(index) ? selectedIndices.filter((i) => i !== index) : [...selectedIndices, index]);
466
+ handleSelectionChange(selectedChartIndices.has(index) ? selectedIndices.filter((i) => i !== index) : [...selectedIndices, index]);
428
467
  }, [
429
468
  onSelectionChange,
430
469
  onSegmentClick,
431
470
  data,
432
471
  selectedIndices,
433
- selectedIndicesSet,
472
+ selectedChartIndices,
434
473
  handleSelectionChange
435
474
  ]);
475
+ const remainingMenuItems = (0, react.useMemo)(() => hasOverflow ? hiddenItems.map((item, index) => {
476
+ var _theme$palette4;
477
+ return {
478
+ name: item.name,
479
+ value: item.value,
480
+ color: item.color || hiddenColors[index] || (theme === null || theme === void 0 || (_theme$palette4 = theme.palette) === null || _theme$palette4 === void 0 || (_theme$palette4 = _theme$palette4.sds) === null || _theme$palette4 === void 0 || (_theme$palette4 = _theme$palette4.base) === null || _theme$palette4 === void 0 ? void 0 : _theme$palette4.ornamentSecondary),
481
+ disabled: item.disabled
482
+ };
483
+ }) : [], [
484
+ hasOverflow,
485
+ hiddenItems,
486
+ hiddenColors,
487
+ theme
488
+ ]);
436
489
  const lastVisibleIndex = (0, react.useMemo)(() => {
437
490
  for (let i = styledItems.length - 1; i >= 0; i--) if (styledItems[i].percentage > 0) return i;
438
491
  return -1;
@@ -444,8 +497,8 @@ const StackedBarChart = (props) => {
444
497
  children: [styledItems.map((item, index) => {
445
498
  return renderBarSegment(item, {
446
499
  barHeight,
447
- hoveredIndex,
448
- selectedIndicesSet,
500
+ hoveredIndex: hoveredChartIndex,
501
+ selectedIndicesSet: selectedChartIndices,
449
502
  selectionBehavior,
450
503
  isLast: index === lastVisibleIndex && !hasRemaining,
451
504
  onMouseEnter: () => {
@@ -461,25 +514,31 @@ const StackedBarChart = (props) => {
461
514
  onClick: () => !item.isExiting && handleSegmentClick(item.originalIndex)
462
515
  });
463
516
  }), previousProcessedRef.current.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BarSegment, {
464
- color: theme === null || theme === void 0 || (_theme$palette4 = theme.palette) === null || _theme$palette4 === void 0 || (_theme$palette4 = _theme$palette4.sds) === null || _theme$palette4 === void 0 || (_theme$palette4 = _theme$palette4.base) === null || _theme$palette4 === void 0 ? void 0 : _theme$palette4.backgroundTertiary,
517
+ color: theme === null || theme === void 0 || (_theme$palette5 = theme.palette) === null || _theme$palette5 === void 0 || (_theme$palette5 = _theme$palette5.sds) === null || _theme$palette5 === void 0 || (_theme$palette5 = _theme$palette5.base) === null || _theme$palette5 === void 0 ? void 0 : _theme$palette5.backgroundTertiary,
465
518
  percentage: displayRemainingPercentage,
466
519
  height: barHeight,
467
520
  isLast: true,
468
- opacity: 1,
521
+ opacity: hasOverflow ? getSegmentOpacity(overflowIndex, hoveredChartIndex, selectedChartIndices, selectionBehavior) : 1,
469
522
  disabled: true
470
523
  }, "remaining")]
471
524
  }), showLegend && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_czi_sds_components.Legend, {
472
525
  items: legendItems,
526
+ lastItemMenuItems: remainingMenuItems,
473
527
  showValues: showLegendValues,
474
528
  onItemMouseEnter: handleLegendItemHover,
475
529
  onItemMouseLeave: handleLegendItemLeave,
476
530
  onItemClick: onLegendItemClick ? handleLegendItemClickInternal : void 0,
477
- selectedIndices,
478
- onSelectionChange: onSelectionChange ? handleSelectionChange : void 0,
479
- hoveredIndex
531
+ selectedIndices: selectedIndices.map(toLegendIndex),
532
+ onSelectionChange: onSelectionChange ? (indices) => handleSelectionChange(indices.map(fromLegendIndex)) : void 0,
533
+ hoveredIndex: hoveredIndex === null ? null : toLegendIndex(hoveredIndex)
480
534
  })] });
481
535
  if (!title) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartWrapper, require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({ width }, rest), {}, { children: chartContent }));
482
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ChartWrapper, require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({ width }, rest), {}, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(TitleContainer, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartTitle, { children: title }), !hideBadge && displayBadge && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledBadge, { children: displayBadge })] }), chartContent] }));
536
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ChartWrapper, require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({ width }, rest), {}, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(TitleContainer, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartTitle, { children: title }), !hideBadge && displayBadge && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_czi_sds_components.Tag, {
537
+ label: displayBadge,
538
+ sdsStyle: "outline",
539
+ color: "neutral",
540
+ size: "small"
541
+ })] }), chartContent] }));
483
542
  };
484
543
  //#endregion
485
544
  Object.defineProperty(exports, "StackedBarChart", {
@@ -3,7 +3,7 @@ import { n as _objectWithoutProperties, t as _objectSpread2 } from "./objectSpre
3
3
  import { cloneElement, useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import styled from "@emotion/styled";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
- import { Legend, Tooltip, TooltipTable, fontHeaderS, fontTabularSemiboldXxxxs, generateDiscreteColors, getCorners, getMode, getSemanticColors, getSpaces } from "@czi-sds/components";
6
+ import { Legend, Tag, Tooltip, TooltipTable, fontBodyMediumXs, generateDiscreteColors, getCorners, getMode, getSemanticColors, getSpaces } from "@czi-sds/components";
7
7
  import { useTheme } from "@mui/material";
8
8
  //#region src/core/StackedBarChart/style.ts
9
9
  const ChartWrapper = styled("div")`
@@ -19,12 +19,12 @@ const TitleContainer = styled("div")`
19
19
  const spaces = getSpaces(props);
20
20
  return `
21
21
  gap: ${spaces === null || spaces === void 0 ? void 0 : spaces.s}px;
22
- margin-bottom: ${spaces === null || spaces === void 0 ? void 0 : spaces.l}px;
22
+ margin-bottom: ${spaces === null || spaces === void 0 ? void 0 : spaces.s}px;
23
23
  `;
24
24
  }}
25
25
  `;
26
26
  const ChartTitle = styled("div")`
27
- ${fontHeaderS}
27
+ ${fontBodyMediumXs}
28
28
 
29
29
  ${(props) => {
30
30
  var _semanticColors$base;
@@ -34,36 +34,19 @@ const ChartTitle = styled("div")`
34
34
  `;
35
35
  }}
36
36
  `;
37
- const StyledBadge = styled("div")`
38
- ${fontTabularSemiboldXxxxs}
39
-
40
- ${(props) => {
41
- var _semanticColors$base2, _semanticColors$base3;
42
- const spaces = getSpaces(props);
43
- const corners = getCorners(props);
44
- const semanticColors = getSemanticColors(props);
45
- return `
46
- padding: ${spaces === null || spaces === void 0 ? void 0 : spaces.xxxs}px ${spaces === null || spaces === void 0 ? void 0 : spaces.xxs}px;
47
- border-radius: ${corners === null || corners === void 0 ? void 0 : corners.m}px;
48
- background-color: ${semanticColors === null || semanticColors === void 0 || (_semanticColors$base2 = semanticColors.base) === null || _semanticColors$base2 === void 0 ? void 0 : _semanticColors$base2.backgroundTertiary};
49
- color: ${semanticColors === null || semanticColors === void 0 || (_semanticColors$base3 = semanticColors.base) === null || _semanticColors$base3 === void 0 ? void 0 : _semanticColors$base3.textSecondary};
50
- `;
51
- }}
52
- `;
53
37
  const BarContainer = styled("div")`
54
38
  ${(props) => {
55
- var _semanticColors$base4;
39
+ var _semanticColors$base2;
56
40
  const { width, barHeight, isEmpty } = props;
57
41
  const corners = getCorners(props);
58
42
  const semanticColors = getSemanticColors(props);
59
43
  const finalWidth = typeof width === "number" ? `${width}px` : width;
60
44
  return `
61
45
  display: flex;
62
- border-radius: ${corners === null || corners === void 0 ? void 0 : corners.s}px;
46
+ border-radius: ${corners === null || corners === void 0 ? void 0 : corners.m}px;
63
47
  width: ${finalWidth};
64
48
  height: ${barHeight}px;
65
- background-color: ${isEmpty ? semanticColors === null || semanticColors === void 0 || (_semanticColors$base4 = semanticColors.base) === null || _semanticColors$base4 === void 0 ? void 0 : _semanticColors$base4.backgroundTertiary : "transparent"};
66
- overflow: hidden;
49
+ background-color: ${isEmpty ? semanticColors === null || semanticColors === void 0 || (_semanticColors$base2 = semanticColors.base) === null || _semanticColors$base2 === void 0 ? void 0 : _semanticColors$base2.backgroundTertiary : "transparent"};
67
50
  `;
68
51
  }}
69
52
  `;
@@ -71,14 +54,17 @@ const BarSegment = styled("div")`
71
54
  ${(props) => {
72
55
  const { color, height, percentage, opacity, disabled, isLast } = props;
73
56
  const spaces = getSpaces(props);
57
+ const corners = getCorners(props);
74
58
  const gap = (spaces === null || spaces === void 0 ? void 0 : spaces.xxxs) || 0;
59
+ const marginRight = percentage > 0 && !isLast ? `${gap}px` : 0;
75
60
  return `
76
61
  background-color: ${color};
62
+ border-radius: ${corners === null || corners === void 0 ? void 0 : corners.m}px;
77
63
  height: ${height}px;
78
64
  flex-grow: ${percentage};
79
65
  flex-basis: 0;
80
66
  flex-shrink: 1;
81
- margin-right: ${percentage > 0 && !isLast ? `${gap}px` : 0};
67
+ margin-right: ${marginRight};
82
68
  opacity: ${opacity};
83
69
  transition: opacity 0.2s ease-in-out, flex-grow 0.25s ease-in-out, margin-right 0.25s ease-out;
84
70
  cursor: ${disabled ? "default" : "pointer"};
@@ -92,7 +78,7 @@ const StyledStackedBarChartWrapper = styled("div")`
92
78
  return `
93
79
  display: flex;
94
80
  flex-direction: column;
95
- gap: ${spaces === null || spaces === void 0 ? void 0 : spaces.m}px;
81
+ gap: ${spaces === null || spaces === void 0 ? void 0 : spaces.s}px;
96
82
  `;
97
83
  }}
98
84
  `;
@@ -122,17 +108,18 @@ const _excluded = [
122
108
  "remainingLabel",
123
109
  "remainingUnit",
124
110
  "unit",
125
- "colorGeneratorOptions"
111
+ "colorGeneratorOptions",
112
+ "maxItems"
126
113
  ];
127
114
  const calculateBadgeText = (totalItems, selectedCount) => {
128
115
  if (selectedCount === 0 || selectedCount === totalItems) return `${totalItems}`;
129
116
  return `${selectedCount} of ${totalItems}`;
130
117
  };
131
118
  const calculateLegendValue = (item, showLegendValues, legendValueFormat, globalUnit) => {
119
+ var _ref, _item$unit;
132
120
  if (!showLegendValues) return void 0;
133
121
  if (legendValueFormat === "percentage") return `${Math.round(item.percentage)}%`;
134
- const effectiveUnit = item.unit || globalUnit;
135
- return effectiveUnit ? `${item.value} ${effectiveUnit}` : `${item.value}`;
122
+ return `${item.value.toLocaleString()} ${(_ref = (_item$unit = item.unit) !== null && _item$unit !== void 0 ? _item$unit : globalUnit) !== null && _ref !== void 0 ? _ref : ""}`.trim();
136
123
  };
137
124
  const getSegmentOpacity = (index, hoveredIndex, selectedIndicesSet, selectionBehavior) => {
138
125
  const hasSelection = selectedIndicesSet.size > 0;
@@ -179,7 +166,7 @@ const applySelectionStyling = (items, selectedIndicesSet, selectionBehavior, mod
179
166
  return mode === "cumulative" ? applyCumulativeHideStyling(items, selectedIndicesSet, selectedTotal, hasRemaining, effectiveMaxAmount) : applyProportionalHideStyling(items, selectedIndicesSet, selectedTotal);
180
167
  };
181
168
  const buildLegendItems = (dataWithPercentages, options) => {
182
- const { showLegendValues, legendValueFormat, unit, hasRemaining, showLegend, remainingValue, remainingPercentage, remainingLabel, remainingUnit, theme } = options;
169
+ const { showLegendValues, legendValueFormat, unit, hasRemaining, showLegend, remainingValue, remainingPercentage, remainingLabel, remainingUnit, remainingOpensMenu, theme } = options;
183
170
  const legendItems = dataWithPercentages.map((item) => ({
184
171
  name: item.name,
185
172
  value: calculateLegendValue(item, showLegendValues, legendValueFormat, unit),
@@ -193,13 +180,13 @@ const buildLegendItems = (dataWithPercentages, options) => {
193
180
  else if (legendValueFormat === "percentage") remainingValueDisplay = `${Math.round(remainingPercentage)}%`;
194
181
  else {
195
182
  const effectiveRemainingUnit = remainingUnit || unit || "";
196
- remainingValueDisplay = effectiveRemainingUnit ? `${remainingValue} ${effectiveRemainingUnit}` : `${remainingValue}`;
183
+ remainingValueDisplay = effectiveRemainingUnit ? `${remainingValue.toLocaleString()} ${effectiveRemainingUnit}` : `${remainingValue.toLocaleString()}`;
197
184
  }
198
185
  legendItems.push({
199
186
  name: remainingLabel,
200
187
  value: remainingValueDisplay,
201
188
  color: theme === null || theme === void 0 || (_theme$palette = theme.palette) === null || _theme$palette === void 0 || (_theme$palette = _theme$palette.sds) === null || _theme$palette === void 0 || (_theme$palette = _theme$palette.base) === null || _theme$palette === void 0 ? void 0 : _theme$palette.backgroundTertiary,
202
- disabled: true
189
+ disabled: !remainingOpensMenu
203
190
  });
204
191
  }
205
192
  return legendItems;
@@ -235,10 +222,13 @@ const renderBarSegment = (item, options) => {
235
222
  }, item.key) : cloneElement(barSegment, { key: item.key });
236
223
  };
237
224
  const StackedBarChart = (props) => {
238
- var _theme$palette4;
239
- const { title, badge, hideBadge = false, data, width = "100%", barHeight: barHeightProp = 16, showLegend = true, showLegendValues = true, legendValueFormat = "percentage", selectedIndices = [], onSelectionChange, onSegmentMouseEnter, onSegmentMouseLeave, onLegendItemMouseEnter, onLegendItemMouseLeave, onSegmentClick, onLegendItemClick, selectionBehavior = "dim", mode = "proportional", maxAmount, remainingLabel = "Remaining", remainingUnit, unit, colorGeneratorOptions } = props, rest = _objectWithoutProperties(props, _excluded);
225
+ var _theme$palette5;
226
+ const { title, badge, hideBadge = false, data, width = "100%", barHeight: barHeightProp = 8, showLegend = true, showLegendValues = true, legendValueFormat = "percentage", selectedIndices = [], onSelectionChange, onSegmentMouseEnter, onSegmentMouseLeave, onLegendItemMouseEnter, onLegendItemMouseLeave, onSegmentClick, onLegendItemClick, selectionBehavior = "dim", mode = "proportional", maxAmount, remainingLabel = "Remaining", remainingUnit, unit, colorGeneratorOptions, maxItems } = props, rest = _objectWithoutProperties(props, _excluded);
240
227
  const barHeight = Math.max(1, barHeightProp);
241
228
  const theme = useTheme();
229
+ const visibleCount = mode === "cumulative" && maxItems !== void 0 && maxItems < data.length ? Math.max(0, maxItems) : data.length;
230
+ const chartData = useMemo(() => data.slice(0, visibleCount), [data, visibleCount]);
231
+ const hiddenItems = useMemo(() => data.slice(visibleCount), [data, visibleCount]);
242
232
  const [hoveredIndex, setHoveredIndex] = useState(null);
243
233
  const [animatedItems, setAnimatedItems] = useState([]);
244
234
  const [autoColorMap, setAutoColorMap] = useState(/* @__PURE__ */ new Map());
@@ -261,8 +251,8 @@ const StackedBarChart = (props) => {
261
251
  theme
262
252
  ]);
263
253
  useEffect(() => {
264
- const autoItems = data.filter((item) => !item.color);
265
- const currentDataKeys = new Set(data.map((item) => item.name));
254
+ const autoItems = chartData.filter((item) => !item.color);
255
+ const currentDataKeys = new Set(chartData.map((item) => item.name));
266
256
  const previousDataKeys = previousDataKeysRef.current;
267
257
  const previousColorOptions = previousColorOptionsRef.current;
268
258
  const colorOptionsChanged = !previousColorOptions || previousColorOptions.isDarkMode !== isDarkMode || previousColorOptions.options !== colorGeneratorOptions;
@@ -288,7 +278,7 @@ const StackedBarChart = (props) => {
288
278
  if (hasChanges) setAutoColorMap(updatedMap);
289
279
  }
290
280
  }, [
291
- data,
281
+ chartData,
292
282
  colorGeneratorOptions,
293
283
  isDarkMode,
294
284
  theme,
@@ -297,9 +287,10 @@ const StackedBarChart = (props) => {
297
287
  const displayBadge = badge !== null && badge !== void 0 ? badge : calculateBadgeText(data.length, selectedIndices.length);
298
288
  const { effectiveMaxAmount, hasRemaining, remainingValue, remainingPercentage } = useMemo(() => {
299
289
  const total = data.reduce((sum, item) => sum + item.value, 0);
290
+ const visibleTotal = chartData.reduce((sum, item) => sum + item.value, 0);
300
291
  const maxAmountValue = mode === "cumulative" && maxAmount ? maxAmount : total;
301
- const hasRemainingSegment = mode === "cumulative" && total < maxAmountValue;
302
- const remaining = hasRemainingSegment ? maxAmountValue - total : 0;
292
+ const hasRemainingSegment = mode === "cumulative" && visibleTotal < maxAmountValue;
293
+ const remaining = hasRemainingSegment ? maxAmountValue - visibleTotal : 0;
303
294
  return {
304
295
  effectiveMaxAmount: maxAmountValue,
305
296
  hasRemaining: hasRemainingSegment,
@@ -308,11 +299,20 @@ const StackedBarChart = (props) => {
308
299
  };
309
300
  }, [
310
301
  data,
302
+ chartData,
311
303
  mode,
312
304
  maxAmount
313
305
  ]);
306
+ const hasOverflow = hasRemaining && hiddenItems.length > 0;
307
+ const overflowIndex = data.length;
308
+ const hiddenColors = useMemo(() => hasOverflow ? generateDiscreteColors(hiddenItems.length, _objectSpread2(_objectSpread2({}, colorGeneratorOptions || {}), {}, { isDarkMode })) : [], [
309
+ hasOverflow,
310
+ hiddenItems.length,
311
+ colorGeneratorOptions,
312
+ isDarkMode
313
+ ]);
314
314
  const dataWithPercentages = useMemo(() => {
315
- return data.map((item, index) => {
315
+ return chartData.map((item, index) => {
316
316
  var _theme$palette3;
317
317
  const resolvedColor = item.color || autoColorMap.get(item.name) || (theme === null || theme === void 0 || (_theme$palette3 = theme.palette) === null || _theme$palette3 === void 0 || (_theme$palette3 = _theme$palette3.sds) === null || _theme$palette3 === void 0 || (_theme$palette3 = _theme$palette3.base) === null || _theme$palette3 === void 0 ? void 0 : _theme$palette3.ornamentSecondary);
318
318
  return _objectSpread2(_objectSpread2({}, item), {}, {
@@ -325,7 +325,7 @@ const StackedBarChart = (props) => {
325
325
  });
326
326
  });
327
327
  }, [
328
- data,
328
+ chartData,
329
329
  effectiveMaxAmount,
330
330
  autoColorMap,
331
331
  theme
@@ -366,10 +366,16 @@ const StackedBarChart = (props) => {
366
366
  timeoutIds.forEach((id) => window.clearTimeout(id));
367
367
  };
368
368
  }, [dataWithPercentages, remainingPercentage]);
369
- const selectedIndicesSet = useMemo(() => new Set(selectedIndices), [selectedIndices]);
370
- const { styledItems, updatedRemainingPercentage, updatedRemainingValue } = useMemo(() => applySelectionStyling(animatedItems, selectedIndicesSet, selectionBehavior, mode, hasRemaining, effectiveMaxAmount, animatedRemainingPercentage, remainingValue), [
369
+ const toChartIndex = useCallback((index) => hasOverflow && index >= visibleCount ? overflowIndex : index, [
370
+ hasOverflow,
371
+ visibleCount,
372
+ overflowIndex
373
+ ]);
374
+ const hoveredChartIndex = hoveredIndex === null ? null : toChartIndex(hoveredIndex);
375
+ const selectedChartIndices = useMemo(() => new Set(selectedIndices.map(toChartIndex)), [selectedIndices, toChartIndex]);
376
+ const { styledItems, updatedRemainingPercentage, updatedRemainingValue } = useMemo(() => applySelectionStyling(animatedItems, selectedChartIndices, selectionBehavior, mode, hasRemaining, effectiveMaxAmount, animatedRemainingPercentage, remainingValue), [
371
377
  animatedItems,
372
- selectedIndicesSet,
378
+ selectedChartIndices,
373
379
  selectionBehavior,
374
380
  mode,
375
381
  hasRemaining,
@@ -390,6 +396,7 @@ const StackedBarChart = (props) => {
390
396
  remainingPercentage: displayRemainingPercentage,
391
397
  remainingLabel,
392
398
  remainingUnit,
399
+ remainingOpensMenu: hasOverflow,
393
400
  theme
394
401
  }), [
395
402
  dataWithPercentages,
@@ -402,19 +409,51 @@ const StackedBarChart = (props) => {
402
409
  displayRemainingPercentage,
403
410
  remainingLabel,
404
411
  remainingUnit,
412
+ hasOverflow,
405
413
  theme
406
414
  ]);
407
- const handleLegendItemHover = useCallback((_item, index) => {
415
+ const toLegendIndex = useCallback((index) => {
416
+ if (!hasOverflow || index < visibleCount) return index;
417
+ return index === overflowIndex ? visibleCount : index + 1;
418
+ }, [
419
+ hasOverflow,
420
+ visibleCount,
421
+ overflowIndex
422
+ ]);
423
+ const fromLegendIndex = useCallback((index) => {
424
+ if (!hasOverflow || index < visibleCount) return index;
425
+ return index === visibleCount ? overflowIndex : index - 1;
426
+ }, [
427
+ hasOverflow,
428
+ visibleCount,
429
+ overflowIndex
430
+ ]);
431
+ const handleLegendItemHover = useCallback((_item, legendIndex) => {
432
+ const index = fromLegendIndex(legendIndex);
408
433
  setHoveredIndex(index);
409
434
  if (onLegendItemMouseEnter && data[index]) onLegendItemMouseEnter(data[index], index);
410
- }, [onLegendItemMouseEnter, data]);
411
- const handleLegendItemLeave = useCallback((_item, index) => {
435
+ }, [
436
+ onLegendItemMouseEnter,
437
+ data,
438
+ fromLegendIndex
439
+ ]);
440
+ const handleLegendItemLeave = useCallback((_item, legendIndex) => {
441
+ const index = fromLegendIndex(legendIndex);
412
442
  setHoveredIndex(null);
413
443
  if (onLegendItemMouseLeave && data[index]) onLegendItemMouseLeave(data[index], index);
414
- }, [onLegendItemMouseLeave, data]);
415
- const handleLegendItemClickInternal = useCallback((_item, index) => {
444
+ }, [
445
+ onLegendItemMouseLeave,
446
+ data,
447
+ fromLegendIndex
448
+ ]);
449
+ const handleLegendItemClickInternal = useCallback((_item, legendIndex) => {
450
+ const index = fromLegendIndex(legendIndex);
416
451
  if (onLegendItemClick && data[index]) onLegendItemClick(data[index], index);
417
- }, [onLegendItemClick, data]);
452
+ }, [
453
+ onLegendItemClick,
454
+ data,
455
+ fromLegendIndex
456
+ ]);
418
457
  const handleSelectionChange = useCallback((newSelectedIndices) => {
419
458
  if (!onSelectionChange) return;
420
459
  onSelectionChange(newSelectedIndices, data.filter((item, itemIndex) => newSelectedIndices.includes(itemIndex)));
@@ -422,15 +461,29 @@ const StackedBarChart = (props) => {
422
461
  const handleSegmentClick = useCallback((index) => {
423
462
  if (onSegmentClick && data[index]) onSegmentClick(data[index], index);
424
463
  if (!onSelectionChange) return;
425
- handleSelectionChange(selectedIndicesSet.has(index) ? selectedIndices.filter((i) => i !== index) : [...selectedIndices, index]);
464
+ handleSelectionChange(selectedChartIndices.has(index) ? selectedIndices.filter((i) => i !== index) : [...selectedIndices, index]);
426
465
  }, [
427
466
  onSelectionChange,
428
467
  onSegmentClick,
429
468
  data,
430
469
  selectedIndices,
431
- selectedIndicesSet,
470
+ selectedChartIndices,
432
471
  handleSelectionChange
433
472
  ]);
473
+ const remainingMenuItems = useMemo(() => hasOverflow ? hiddenItems.map((item, index) => {
474
+ var _theme$palette4;
475
+ return {
476
+ name: item.name,
477
+ value: item.value,
478
+ color: item.color || hiddenColors[index] || (theme === null || theme === void 0 || (_theme$palette4 = theme.palette) === null || _theme$palette4 === void 0 || (_theme$palette4 = _theme$palette4.sds) === null || _theme$palette4 === void 0 || (_theme$palette4 = _theme$palette4.base) === null || _theme$palette4 === void 0 ? void 0 : _theme$palette4.ornamentSecondary),
479
+ disabled: item.disabled
480
+ };
481
+ }) : [], [
482
+ hasOverflow,
483
+ hiddenItems,
484
+ hiddenColors,
485
+ theme
486
+ ]);
434
487
  const lastVisibleIndex = useMemo(() => {
435
488
  for (let i = styledItems.length - 1; i >= 0; i--) if (styledItems[i].percentage > 0) return i;
436
489
  return -1;
@@ -442,8 +495,8 @@ const StackedBarChart = (props) => {
442
495
  children: [styledItems.map((item, index) => {
443
496
  return renderBarSegment(item, {
444
497
  barHeight,
445
- hoveredIndex,
446
- selectedIndicesSet,
498
+ hoveredIndex: hoveredChartIndex,
499
+ selectedIndicesSet: selectedChartIndices,
447
500
  selectionBehavior,
448
501
  isLast: index === lastVisibleIndex && !hasRemaining,
449
502
  onMouseEnter: () => {
@@ -459,25 +512,31 @@ const StackedBarChart = (props) => {
459
512
  onClick: () => !item.isExiting && handleSegmentClick(item.originalIndex)
460
513
  });
461
514
  }), previousProcessedRef.current.length > 0 && /* @__PURE__ */ jsx(BarSegment, {
462
- color: theme === null || theme === void 0 || (_theme$palette4 = theme.palette) === null || _theme$palette4 === void 0 || (_theme$palette4 = _theme$palette4.sds) === null || _theme$palette4 === void 0 || (_theme$palette4 = _theme$palette4.base) === null || _theme$palette4 === void 0 ? void 0 : _theme$palette4.backgroundTertiary,
515
+ color: theme === null || theme === void 0 || (_theme$palette5 = theme.palette) === null || _theme$palette5 === void 0 || (_theme$palette5 = _theme$palette5.sds) === null || _theme$palette5 === void 0 || (_theme$palette5 = _theme$palette5.base) === null || _theme$palette5 === void 0 ? void 0 : _theme$palette5.backgroundTertiary,
463
516
  percentage: displayRemainingPercentage,
464
517
  height: barHeight,
465
518
  isLast: true,
466
- opacity: 1,
519
+ opacity: hasOverflow ? getSegmentOpacity(overflowIndex, hoveredChartIndex, selectedChartIndices, selectionBehavior) : 1,
467
520
  disabled: true
468
521
  }, "remaining")]
469
522
  }), showLegend && /* @__PURE__ */ jsx(Legend, {
470
523
  items: legendItems,
524
+ lastItemMenuItems: remainingMenuItems,
471
525
  showValues: showLegendValues,
472
526
  onItemMouseEnter: handleLegendItemHover,
473
527
  onItemMouseLeave: handleLegendItemLeave,
474
528
  onItemClick: onLegendItemClick ? handleLegendItemClickInternal : void 0,
475
- selectedIndices,
476
- onSelectionChange: onSelectionChange ? handleSelectionChange : void 0,
477
- hoveredIndex
529
+ selectedIndices: selectedIndices.map(toLegendIndex),
530
+ onSelectionChange: onSelectionChange ? (indices) => handleSelectionChange(indices.map(fromLegendIndex)) : void 0,
531
+ hoveredIndex: hoveredIndex === null ? null : toLegendIndex(hoveredIndex)
478
532
  })] });
479
533
  if (!title) return /* @__PURE__ */ jsx(ChartWrapper, _objectSpread2(_objectSpread2({ width }, rest), {}, { children: chartContent }));
480
- return /* @__PURE__ */ jsxs(ChartWrapper, _objectSpread2(_objectSpread2({ width }, rest), {}, { children: [/* @__PURE__ */ jsxs(TitleContainer, { children: [/* @__PURE__ */ jsx(ChartTitle, { children: title }), !hideBadge && displayBadge && /* @__PURE__ */ jsx(StyledBadge, { children: displayBadge })] }), chartContent] }));
534
+ return /* @__PURE__ */ jsxs(ChartWrapper, _objectSpread2(_objectSpread2({ width }, rest), {}, { children: [/* @__PURE__ */ jsxs(TitleContainer, { children: [/* @__PURE__ */ jsx(ChartTitle, { children: title }), !hideBadge && displayBadge && /* @__PURE__ */ jsx(Tag, {
535
+ label: displayBadge,
536
+ sdsStyle: "outline",
537
+ color: "neutral",
538
+ size: "small"
539
+ })] }), chartContent] }));
481
540
  };
482
541
  //#endregion
483
542
  export { StackedBarChart as t };
@@ -361,6 +361,17 @@ interface ProteinStructureViewerProps extends Omit<HTMLAttributes<HTMLDivElement
361
361
  * @default true
362
362
  */
363
363
  showSequenceViewer?: boolean;
364
+ /**
365
+ * Height of the sequence panel, as any CSS length; a number is read as
366
+ * pixels, and a percentage is of the viewer's own height. The 3D view and the
367
+ * legend move up to make room for it. Ignored while `showSequenceViewer` is
368
+ * off.
369
+ *
370
+ * Left undefined, the panel takes `max(104px, 30%)` of the viewer, growing to
371
+ * `max(134px, 32%)` once the viewer is 880px wide or more. A height set here
372
+ * holds at every width.
373
+ */
374
+ sequenceViewerHeight?: number | string;
364
375
  /**
365
376
  * Show the stats and color scale legend overlaid on the viewer.
366
377
  * @default true
@@ -361,6 +361,17 @@ interface ProteinStructureViewerProps extends Omit<HTMLAttributes<HTMLDivElement
361
361
  * @default true
362
362
  */
363
363
  showSequenceViewer?: boolean;
364
+ /**
365
+ * Height of the sequence panel, as any CSS length; a number is read as
366
+ * pixels, and a percentage is of the viewer's own height. The 3D view and the
367
+ * legend move up to make room for it. Ignored while `showSequenceViewer` is
368
+ * off.
369
+ *
370
+ * Left undefined, the panel takes `max(104px, 30%)` of the viewer, growing to
371
+ * `max(134px, 32%)` once the viewer is 880px wide or more. A height set here
372
+ * holds at every width.
373
+ */
374
+ sequenceViewerHeight?: number | string;
364
375
  /**
365
376
  * Show the stats and color scale legend overlaid on the viewer.
366
377
  * @default true
@@ -64,7 +64,7 @@ interface StackedBarChartProps extends HTMLAttributes<HTMLDivElement> {
64
64
  /**
65
65
  * Height of the bar in pixels. Values below 1 are clamped to 1, so the bar
66
66
  * cannot disappear.
67
- * @default 16
67
+ * @default 8
68
68
  */
69
69
  barHeight?: number;
70
70
  /**
@@ -139,6 +139,12 @@ interface StackedBarChartProps extends HTMLAttributes<HTMLDivElement> {
139
139
  * between it and the sum of the data is drawn as the Remaining segment.
140
140
  */
141
141
  maxAmount?: number;
142
+ /**
143
+ * Cumulative mode only. How many items are drawn as their own segment. The
144
+ * rest fold into the Remaining segment, whose legend item opens a menu
145
+ * listing them.
146
+ */
147
+ maxItems?: number;
142
148
  /**
143
149
  * Name of the grey gap segment in cumulative mode.
144
150
  * @default "Remaining"
@@ -64,7 +64,7 @@ interface StackedBarChartProps extends HTMLAttributes<HTMLDivElement> {
64
64
  /**
65
65
  * Height of the bar in pixels. Values below 1 are clamped to 1, so the bar
66
66
  * cannot disappear.
67
- * @default 16
67
+ * @default 8
68
68
  */
69
69
  barHeight?: number;
70
70
  /**
@@ -139,6 +139,12 @@ interface StackedBarChartProps extends HTMLAttributes<HTMLDivElement> {
139
139
  * between it and the sum of the data is drawn as the Remaining segment.
140
140
  */
141
141
  maxAmount?: number;
142
+ /**
143
+ * Cumulative mode only. How many items are drawn as their own segment. The
144
+ * rest fold into the Remaining segment, whose legend item opens a menu
145
+ * listing them.
146
+ */
147
+ maxItems?: number;
142
148
  /**
143
149
  * Name of the grey gap segment in cumulative mode.
144
150
  * @default "Remaining"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@czi-sds/data-viz",
3
- "version": "2.5.0",
3
+ "version": "3.0.0",
4
4
  "main": "dist/index.cjs.js",
5
5
  "types": "dist/index.cjs.d.ts",
6
6
  "module": "dist/index.esm.js",
@@ -121,7 +121,7 @@
121
121
  "namespace-check": "tsc --p tsconfig-namespace-check.json"
122
122
  },
123
123
  "peerDependencies": {
124
- "@czi-sds/components": "^24.0.0",
124
+ "@czi-sds/components": "^25.0.0",
125
125
  "@emotion/styled": "^11.11.0",
126
126
  "@mui/material": "^9.0.0",
127
127
  "@phosphor-icons/react": "^2.1.10",
@@ -157,5 +157,5 @@
157
157
  "echarts": "^6.0.0",
158
158
  "lodash": "^4.17.21"
159
159
  },
160
- "gitHead": "af9ead042355f832da2c98e7dc3ecaafd1ea1664"
160
+ "gitHead": "d38c18e3aca773081cb5c0413dbfefb10da774b8"
161
161
  }