td-plots 1.6.1 → 1.7.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.
@@ -16,7 +16,7 @@ export type HistogramPlotProps = {
16
16
  plotId?: string;
17
17
  selectByBin?: boolean;
18
18
  dateTickFormat?: string;
19
- binSizeOverride?: number;
19
+ binSizeOverride?: number | string;
20
20
  statsAnnotations?: HistogramStatsAnnotation[];
21
21
  emptySelectedRange?: boolean;
22
22
  d3FormatValueString?: string;
@@ -11,6 +11,7 @@ export type PlusMinusControlProps = {
11
11
  colorOverride?: string;
12
12
  valueLabelFormat?: (value: number) => string;
13
13
  titleStylingOverrides?: React.CSSProperties;
14
+ disabledTooltipText?: string;
14
15
  };
15
16
  declare const PlusMinusControl: React.FC<PlusMinusControlProps>;
16
17
  export default PlusMinusControl;
@@ -0,0 +1,11 @@
1
+ export type StatsDonutProps = {
2
+ values: number[];
3
+ labels: string[];
4
+ hole: number;
5
+ withCenterLabel?: boolean;
6
+ centerLabel?: string;
7
+ centerValue?: string;
8
+ colors?: string[];
9
+ showLegend?: boolean;
10
+ };
11
+ export declare const StatsDonut: (props: StatsDonutProps) => import("react/jsx-runtime").JSX.Element;
package/dist/index.d.ts CHANGED
@@ -3,6 +3,8 @@ export { HistogramPlot } from "./components/Histogram";
3
3
  export type { HistogramPlotProps } from "./components/Histogram";
4
4
  export { RadialHistogramPlot } from "./components/RadialHistogram";
5
5
  export type { RadialHistogramPlotProps } from "./components/RadialHistogram";
6
+ export { StatsDonut } from "./components/StatsDonut";
7
+ export type { StatsDonutProps } from "./components/StatsDonut";
6
8
  export { default as TestPlot } from "./components/TestPlot";
7
9
  export { isDateArray, isNumberArray } from "./components/Utils";
8
10
  export type { PlotParams } from "react-plotly.js";
package/dist/index.esm.js CHANGED
@@ -3,6 +3,7 @@ import React, { useEffect, lazy, useRef, useState, useMemo, Suspense } from 'rea
3
3
  import CircularProgress from '@mui/material/CircularProgress';
4
4
  import ButtonGroup from '@mui/material/ButtonGroup';
5
5
  import Button from '@mui/material/Button';
6
+ import Tooltip from '@mui/material/Tooltip';
6
7
  import { createSvgIcon } from '@mui/material/utils';
7
8
  import IconButton from '@mui/material/IconButton';
8
9
 
@@ -435,7 +436,7 @@ var RemoveIcon = createSvgIcon(/*#__PURE__*/jsx("path", {
435
436
  d: "M19 13H5v-2h14z"
436
437
  }), 'Remove');
437
438
 
438
- const PlusMinusControl = ({ value, onChange, steps = [1], ariaLabel = "Plus Minus Control", disabled = false, showValue = true, title = "", themeRole = "primary", colorOverride = undefined, valueLabelFormat, titleStylingOverrides = {}, }) => {
439
+ const PlusMinusControl = ({ value, onChange, steps = [1], ariaLabel = "Plus Minus Control", disabled = false, showValue = true, title = "", themeRole = "primary", colorOverride = undefined, valueLabelFormat, titleStylingOverrides = {}, disabledTooltipText = "Insufficient data", }) => {
439
440
  const color = colorOverride || "#111fbcff";
440
441
  const [disablePlus, setDisablePlus] = React.useState(false);
441
442
  const [disableMinus, setDisableMinus] = React.useState(false);
@@ -462,39 +463,40 @@ const PlusMinusControl = ({ value, onChange, steps = [1], ariaLabel = "Plus Minu
462
463
  const newValue = stepIndex === -1 ? steps[0] : steps[Math.max(stepIndex - 1, 0)];
463
464
  onChange(newValue);
464
465
  };
466
+ const buttonGroupContent = (jsxs(ButtonGroup, { size: "small", "aria-label": ariaLabel, variant: "text", disabled: disabled, orientation: "horizontal", color: themeRole, sx: {
467
+ backgroundColor: "background.paper",
468
+ "& .MuiButton-root:not(.Mui-disabled)": {
469
+ color: color,
470
+ },
471
+ "& .MuiButton-root:not(.Mui-disabled) .MuiSvgIcon-root": {
472
+ color: color,
473
+ },
474
+ }, children: [jsx(Button, { disabled: true, "aria-label": `${title}`, sx: Object.assign({ paddingRight: "10px", "&.Mui-disabled": disabled
475
+ ? {}
476
+ : {
477
+ color: `${color} !important`,
478
+ }, "& .MuiSvgIcon-root": {
479
+ color: color,
480
+ backgroundColor: color,
481
+ } }, titleStylingOverrides), children: title }), showValue && (jsx(Button, { "aria-label": "current value", sx: {
482
+ pointerEvents: "none",
483
+ fontWeight: "600",
484
+ minHeight: "32px",
485
+ maxHeight: "32px",
486
+ height: "32px",
487
+ }, children: valueLabelFormat ? valueLabelFormat(value) : value })), jsx(Button, { onClick: handleIncrement, "aria-label": "increment", disabled: disablePlus, children: jsx(AddIcon, {}) }), jsx(Button, { onClick: handleDecrement, "aria-label": "decrement", disabled: disableMinus, children: jsx(RemoveIcon, {}) })] }));
465
488
  return (jsx("div", { style: {
466
489
  display: "flex",
467
490
  flexDirection: "row",
468
491
  alignItems: "center",
469
- }, children: jsxs(ButtonGroup, { size: "small", "aria-label": ariaLabel, variant: "text", disabled: disabled, orientation: "horizontal", color: themeRole, sx: {
470
- backgroundColor: "background.paper",
471
- "& .MuiButton-root:not(.Mui-disabled)": {
472
- color: color,
473
- },
474
- "& .MuiButton-root:not(.Mui-disabled) .MuiSvgIcon-root": {
475
- color: color,
476
- },
477
- }, children: [jsx(Button, { disabled: true, "aria-label": `${title}`, sx: Object.assign({ paddingRight: "10px", "&.Mui-disabled": disabled
478
- ? {}
479
- : {
480
- color: `${color} !important`,
481
- }, "& .MuiSvgIcon-root": {
482
- color: color,
483
- backgroundColor: color,
484
- } }, titleStylingOverrides), children: title }), showValue && (jsx(Button, { "aria-label": "current value", sx: {
485
- pointerEvents: "none",
486
- fontWeight: "600",
487
- minHeight: "32px",
488
- maxHeight: "32px",
489
- height: "32px",
490
- }, children: valueLabelFormat ? valueLabelFormat(value) : value })), jsx(Button, { onClick: handleIncrement, "aria-label": "increment", disabled: disablePlus, children: jsx(AddIcon, {}) }), jsx(Button, { onClick: handleDecrement, "aria-label": "decrement", disabled: disableMinus, children: jsx(RemoveIcon, {}) })] }) }));
492
+ }, children: disabled ? (jsx(Tooltip, { title: disabledTooltipText, arrow: true, children: jsx("span", { children: buttonGroupContent }) })) : (buttonGroupContent) }));
491
493
  };
492
494
 
493
495
  var SettingsIcon = createSvgIcon(/*#__PURE__*/jsx("path", {
494
496
  d: "M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6"
495
497
  }), 'Settings');
496
498
 
497
- const Plot$2 = lazy(() => import('react-plotly.js'));
499
+ const Plot$3 = lazy(() => import('react-plotly.js'));
498
500
  const HistogramPlot = (props) => {
499
501
  var _a, _b, _c, _d;
500
502
  const { data, title, xAxisTitle, barColor = "rgb(72, 72, 74)", unselectedBarColor = "rgba(203, 195, 195, 0.88)", selectorsColor = "black", containerStyleOverrides, unselectedData = [], handleClickOrSelection = () => { }, onDeselect = () => { }, plotId, selectByBin = false, dateTickFormat, binSizeOverride, statsAnnotations = ["mean"], emptySelectedRange = false, d3FormatValueString = ".1f", showBinSizeControls = "always", onBinSizeCalculated, onBinSizeChange, showBinSizeControlValue = true, isMobile = false, settingsTitleStylingOverrides = {}, } = props;
@@ -1003,10 +1005,14 @@ const HistogramPlot = (props) => {
1003
1005
  const isDivisibleByBinSize = isDateArray(data)
1004
1006
  ? (new Date(fixedXAxisRange[1]).getTime() -
1005
1007
  new Date(fixedXAxisRange[0]).getTime()) %
1006
- activeBinSize ===
1008
+ (typeof activeBinSize === "number"
1009
+ ? activeBinSize
1010
+ : plotlyMToMilliseconds(activeBinSize)) ===
1007
1011
  0
1008
1012
  : (fixedXAxisRange[1] - fixedXAxisRange[0]) %
1009
- activeBinSize ===
1013
+ (typeof activeBinSize === "number"
1014
+ ? activeBinSize
1015
+ : plotlyMToMilliseconds(activeBinSize)) ===
1010
1016
  0;
1011
1017
  if (!isDivisibleByBinSize) {
1012
1018
  // Need to extend the axis range to the next bin edge.
@@ -1169,11 +1175,29 @@ const HistogramPlot = (props) => {
1169
1175
  };
1170
1176
  }
1171
1177
  }
1178
+ else if (defaultBinSize && typeof defaultBinSize === "string") {
1179
+ // Then we have a non-numeric bin size. It's a plotly M string
1180
+ // So we know we're dealing with months.
1181
+ const binSliderMarkValues = [1, 2, 3, 6, 12]
1182
+ .map((n) => n * ONEAVGMONTH)
1183
+ .concat([plotlyMToMilliseconds(defaultBinSize)]);
1184
+ const uniqueMarks = [...new Set(binSliderMarkValues)];
1185
+ binSliderMarks = uniqueMarks
1186
+ .sort((a, b) => a - b)
1187
+ .map((val) => ({
1188
+ value: val,
1189
+ label: `${Math.round(val / ONEAVGMONTH)} month${Math.round(val / ONEAVGMONTH) > 1 ? "s" : ""}`,
1190
+ }));
1191
+ valueLabelFormat = (value) => {
1192
+ const valueLabel = `${Math.round(value / ONEAVGMONTH)} month${Math.round(value / ONEAVGMONTH) > 1 ? "s" : ""}`;
1193
+ return valueLabel;
1194
+ };
1195
+ }
1172
1196
  return (jsx("div", { ref: containerRef, className: `plot-container ${plotId}`, style: Object.assign({ "--selection-color": selectorsColor }, containerStyles), children: jsx(Suspense, { fallback: jsx(Loading, {}), children: jsxs("div", { style: {
1173
1197
  position: "relative",
1174
1198
  width: "100%",
1175
1199
  height: "100%",
1176
- }, children: [showBinSizeControls !== "never" && typeof binSize === "number" && (jsxs("div", { className: `histogram-controls ${showBinSizeControls === "onHover"
1200
+ }, children: [showBinSizeControls !== "never" && (jsxs("div", { className: `histogram-controls ${showBinSizeControls === "onHover"
1177
1201
  ? "show-on-hover"
1178
1202
  : "show-always"}`, style: {
1179
1203
  position: "absolute",
@@ -1212,13 +1236,17 @@ const HistogramPlot = (props) => {
1212
1236
  transition: isMobile
1213
1237
  ? ""
1214
1238
  : "max-width 0.15s ease-in-out, opacity 0.15s ease-in-out",
1215
- }, children: jsx("div", { children: jsx(PlusMinusControl, { "aria-label": "Bin Size Control", disabled: data.length < 10, value: activeBinSize, steps: binSliderMarks
1239
+ }, children: jsx("div", { children: jsx(PlusMinusControl, { "aria-label": "Bin Size Control", disabled: allData.length < 10, value: activeBinSize
1240
+ ? typeof activeBinSize === "number"
1241
+ ? activeBinSize
1242
+ : plotlyMToMilliseconds(activeBinSize)
1243
+ : 0, steps: binSliderMarks
1216
1244
  ? binSliderMarks.map((mark) => mark.value)
1217
1245
  : [1], title: isMobile ? "Res" : "Resolution", showValue: showBinSizeControlValue, onChange: (newValue) => {
1218
1246
  const newBinSize = newValue;
1219
1247
  setBinSize(newBinSize);
1220
1248
  onBinSizeChange === null || onBinSizeChange === void 0 ? void 0 : onBinSizeChange(newBinSize);
1221
- }, colorOverride: barColor, valueLabelFormat: valueLabelFormat, titleStylingOverrides: settingsTitleStylingOverrides }) }) })] })), jsx(Plot$2, { data: plotlyData, layout: layout, config: config, onSelected: handleSelection, onClick: handleClick, onDeselect: () => {
1249
+ }, colorOverride: barColor, valueLabelFormat: valueLabelFormat, titleStylingOverrides: settingsTitleStylingOverrides, disabledTooltipText: "Requires 10 or more data points" }) }) })] })), jsx(Plot$3, { data: plotlyData, layout: layout, config: config, onSelected: handleSelection, onClick: handleClick, onDeselect: () => {
1222
1250
  onDeselect();
1223
1251
  setSelectedRange(null); // Remove selected box
1224
1252
  }, onUpdate: handlePlotUpdate, useResizeHandler: true, style: {
@@ -1229,7 +1257,7 @@ const HistogramPlot = (props) => {
1229
1257
  } }, `histogram-${plotId || "default"}`)] }) }) }));
1230
1258
  };
1231
1259
 
1232
- const Plot$1 = lazy(() => import('react-plotly.js'));
1260
+ const Plot$2 = lazy(() => import('react-plotly.js'));
1233
1261
  const RadialHistogramPlot = (props) => {
1234
1262
  const { data, barColor = 'rgb(72, 72, 74)', unselectedBarColor = 'rgba(203, 195, 195, 0.88)', selectorsColor = 'black', onSelected, onClick, containerStyleOverrides, barWidth = 20, // Default bar width in degrees
1235
1263
  } = props;
@@ -1329,13 +1357,89 @@ const RadialHistogramPlot = (props) => {
1329
1357
  staticPlot: false,
1330
1358
  };
1331
1359
  const containerStyles = Object.assign({ width: "100%", height: "100%", position: "relative" }, containerStyleOverrides);
1332
- return (jsx("div", { ref: containerRef, className: "plot-container radial-histogram-container", style: Object.assign({ '--selection-color': selectorsColor }, containerStyles), children: jsx(Suspense, { fallback: jsx(Loading, {}), children: jsx(Plot$1, { data: plotlyData, layout: layout, config: config, onSelected: onSelected, onClick: onClick, useResizeHandler: true, style: {
1360
+ return (jsx("div", { ref: containerRef, className: "plot-container radial-histogram-container", style: Object.assign({ '--selection-color': selectorsColor }, containerStyles), children: jsx(Suspense, { fallback: jsx(Loading, {}), children: jsx(Plot$2, { data: plotlyData, layout: layout, config: config, onSelected: onSelected, onClick: onClick, useResizeHandler: true, style: {
1333
1361
  width: "100%",
1334
1362
  height: "100%",
1335
1363
  display: "block"
1336
1364
  } }) }) }));
1337
1365
  };
1338
1366
 
1367
+ const Plot$1 = lazy(() => import('react-plotly.js'));
1368
+ const StatsDonut = (props) => {
1369
+ const { withCenterLabel = false, centerLabel, centerValue, showLegend = true, } = props;
1370
+ const annotations = withCenterLabel
1371
+ ? [
1372
+ {
1373
+ text: centerLabel || "",
1374
+ x: 0.5,
1375
+ y: 0.55,
1376
+ xref: "paper",
1377
+ yref: "paper",
1378
+ showarrow: false,
1379
+ font: {
1380
+ size: 16,
1381
+ color: "#666",
1382
+ },
1383
+ },
1384
+ {
1385
+ text: centerValue || "",
1386
+ x: 0.5,
1387
+ y: 0.45,
1388
+ xref: "paper",
1389
+ yref: "paper",
1390
+ showarrow: false,
1391
+ font: {
1392
+ size: 24,
1393
+ weight: "bold",
1394
+ color: "#000",
1395
+ },
1396
+ },
1397
+ ]
1398
+ : [];
1399
+ return (jsx("div", { style: {
1400
+ height: "400px",
1401
+ width: "600px",
1402
+ display: "flex",
1403
+ justifyContent: "center",
1404
+ alignItems: "center",
1405
+ }, children: jsx(Suspense, { fallback: jsx(Loading, {}), children: jsx(Plot$1, { data: [
1406
+ {
1407
+ type: "pie",
1408
+ values: props.values,
1409
+ labels: props.labels,
1410
+ hole: props.hole,
1411
+ marker: props.colors
1412
+ ? {
1413
+ colors: props.colors,
1414
+ }
1415
+ : undefined,
1416
+ },
1417
+ ], layout: {
1418
+ showlegend: true,
1419
+ annotations,
1420
+ height: 400,
1421
+ width: 600,
1422
+ legend: {
1423
+ x: 1.3,
1424
+ y: 0.5,
1425
+ font: {
1426
+ size: 16,
1427
+ },
1428
+ },
1429
+ margin: {
1430
+ l: 25,
1431
+ r: 15,
1432
+ t: 0,
1433
+ b: 0,
1434
+ },
1435
+ }, style: {
1436
+ width: "100%",
1437
+ height: "100%",
1438
+ }, config: {
1439
+ displayModeBar: false,
1440
+ } }) }) }));
1441
+ };
1442
+
1339
1443
  const Plot = lazy(() => import('react-plotly.js'));
1340
1444
  const TestPlot = (props) => {
1341
1445
  var _a, _b;
@@ -1355,5 +1459,5 @@ const TestPlot = (props) => {
1355
1459
  return jsx(Plot, { data: data, layout: layout });
1356
1460
  };
1357
1461
 
1358
- export { HistogramPlot, RadialHistogramPlot, TestPlot, isDateArray, isNumberArray };
1462
+ export { HistogramPlot, RadialHistogramPlot, StatsDonut, TestPlot, isDateArray, isNumberArray };
1359
1463
  //# sourceMappingURL=index.esm.js.map