td-plots 1.5.4 → 1.5.5

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.
@@ -18,6 +18,7 @@ export type HistogramPlotProps = {
18
18
  binSizeOverride?: number;
19
19
  statsAnnotations?: HistogramStatsAnnotation[];
20
20
  emptySelectedRange?: boolean;
21
+ d3FormatValueString?: string;
21
22
  };
22
23
  export declare const HistogramPlot: (props: HistogramPlotProps) => import("react/jsx-runtime").JSX.Element;
23
24
  export default HistogramPlot;
package/dist/index.esm.js CHANGED
@@ -36,6 +36,319 @@ function styleInject(css, ref) {
36
36
  var css_248z = ".plot-container{height:100%;max-width:100%;min-height:300px;overflow:hidden!important;position:relative;width:100%}.plot-container>div{flex:1;height:100%!important;width:100%!important}.plot-container .main-svg{max-height:100%!important;max-width:100%!important}.plot-container .main-svg,.plot-container .plotly-graph-div,.plot-container svg.main-svg[height],.plot-container svg.main-svg[width]{height:100%!important;width:100%!important}.plot-container .point{border-radius:5px!important;overflow:hidden!important}.plot-container .cursor-ns-resize{height:0;width:0}.plot-container .cursor-ew-resize{fill:var(--selection-color,blue)!important;stroke:var(--selection-color,blue)!important}.plot-container .selectionlayer>path{stroke:var(--selection-color,blue)!important;stroke-dasharray:0!important;stroke-width:1px!important;opacity:.5!important}.plot-container .zoomlayer>path{stroke-dasharray:0!important;stroke:var(--selection-color,blue)!important;fill:var(--selection-color,blue)!important}.radial-histogram-container{aspect-ratio:1}.loading-overlay{align-items:center;background-color:hsla(0,0%,100%,.8);display:flex;height:100%;justify-content:center;left:0;position:absolute;top:0;width:100%;z-index:300}";
37
37
  styleInject(css_248z);
38
38
 
39
+ function formatDecimal(x) {
40
+ return Math.abs(x = Math.round(x)) >= 1e21
41
+ ? x.toLocaleString("en").replace(/,/g, "")
42
+ : x.toString(10);
43
+ }
44
+
45
+ // Computes the decimal coefficient and exponent of the specified number x with
46
+ // significant digits p, where x is positive and p is in [1, 21] or undefined.
47
+ // For example, formatDecimalParts(1.23) returns ["123", 0].
48
+ function formatDecimalParts(x, p) {
49
+ if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
50
+ var i, coefficient = x.slice(0, i);
51
+
52
+ // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
53
+ // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
54
+ return [
55
+ coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,
56
+ +x.slice(i + 1)
57
+ ];
58
+ }
59
+
60
+ function exponent(x) {
61
+ return x = formatDecimalParts(Math.abs(x)), x ? x[1] : NaN;
62
+ }
63
+
64
+ function formatGroup(grouping, thousands) {
65
+ return function(value, width) {
66
+ var i = value.length,
67
+ t = [],
68
+ j = 0,
69
+ g = grouping[0],
70
+ length = 0;
71
+
72
+ while (i > 0 && g > 0) {
73
+ if (length + g + 1 > width) g = Math.max(1, width - length);
74
+ t.push(value.substring(i -= g, i + g));
75
+ if ((length += g + 1) > width) break;
76
+ g = grouping[j = (j + 1) % grouping.length];
77
+ }
78
+
79
+ return t.reverse().join(thousands);
80
+ };
81
+ }
82
+
83
+ function formatNumerals(numerals) {
84
+ return function(value) {
85
+ return value.replace(/[0-9]/g, function(i) {
86
+ return numerals[+i];
87
+ });
88
+ };
89
+ }
90
+
91
+ // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
92
+ var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
93
+
94
+ function formatSpecifier(specifier) {
95
+ if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
96
+ var match;
97
+ return new FormatSpecifier({
98
+ fill: match[1],
99
+ align: match[2],
100
+ sign: match[3],
101
+ symbol: match[4],
102
+ zero: match[5],
103
+ width: match[6],
104
+ comma: match[7],
105
+ precision: match[8] && match[8].slice(1),
106
+ trim: match[9],
107
+ type: match[10]
108
+ });
109
+ }
110
+
111
+ formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
112
+
113
+ function FormatSpecifier(specifier) {
114
+ this.fill = specifier.fill === undefined ? " " : specifier.fill + "";
115
+ this.align = specifier.align === undefined ? ">" : specifier.align + "";
116
+ this.sign = specifier.sign === undefined ? "-" : specifier.sign + "";
117
+ this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + "";
118
+ this.zero = !!specifier.zero;
119
+ this.width = specifier.width === undefined ? undefined : +specifier.width;
120
+ this.comma = !!specifier.comma;
121
+ this.precision = specifier.precision === undefined ? undefined : +specifier.precision;
122
+ this.trim = !!specifier.trim;
123
+ this.type = specifier.type === undefined ? "" : specifier.type + "";
124
+ }
125
+
126
+ FormatSpecifier.prototype.toString = function() {
127
+ return this.fill
128
+ + this.align
129
+ + this.sign
130
+ + this.symbol
131
+ + (this.zero ? "0" : "")
132
+ + (this.width === undefined ? "" : Math.max(1, this.width | 0))
133
+ + (this.comma ? "," : "")
134
+ + (this.precision === undefined ? "" : "." + Math.max(0, this.precision | 0))
135
+ + (this.trim ? "~" : "")
136
+ + this.type;
137
+ };
138
+
139
+ // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
140
+ function formatTrim(s) {
141
+ out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
142
+ switch (s[i]) {
143
+ case ".": i0 = i1 = i; break;
144
+ case "0": if (i0 === 0) i0 = i; i1 = i; break;
145
+ default: if (!+s[i]) break out; if (i0 > 0) i0 = 0; break;
146
+ }
147
+ }
148
+ return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
149
+ }
150
+
151
+ var prefixExponent;
152
+
153
+ function formatPrefixAuto(x, p) {
154
+ var d = formatDecimalParts(x, p);
155
+ if (!d) return x + "";
156
+ var coefficient = d[0],
157
+ exponent = d[1],
158
+ i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
159
+ n = coefficient.length;
160
+ return i === n ? coefficient
161
+ : i > n ? coefficient + new Array(i - n + 1).join("0")
162
+ : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i)
163
+ : "0." + new Array(1 - i).join("0") + formatDecimalParts(x, Math.max(0, p + i - 1))[0]; // less than 1y!
164
+ }
165
+
166
+ function formatRounded(x, p) {
167
+ var d = formatDecimalParts(x, p);
168
+ if (!d) return x + "";
169
+ var coefficient = d[0],
170
+ exponent = d[1];
171
+ return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient
172
+ : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1)
173
+ : coefficient + new Array(exponent - coefficient.length + 2).join("0");
174
+ }
175
+
176
+ var formatTypes = {
177
+ "%": (x, p) => (x * 100).toFixed(p),
178
+ "b": (x) => Math.round(x).toString(2),
179
+ "c": (x) => x + "",
180
+ "d": formatDecimal,
181
+ "e": (x, p) => x.toExponential(p),
182
+ "f": (x, p) => x.toFixed(p),
183
+ "g": (x, p) => x.toPrecision(p),
184
+ "o": (x) => Math.round(x).toString(8),
185
+ "p": (x, p) => formatRounded(x * 100, p),
186
+ "r": formatRounded,
187
+ "s": formatPrefixAuto,
188
+ "X": (x) => Math.round(x).toString(16).toUpperCase(),
189
+ "x": (x) => Math.round(x).toString(16)
190
+ };
191
+
192
+ function identity(x) {
193
+ return x;
194
+ }
195
+
196
+ var map = Array.prototype.map,
197
+ prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];
198
+
199
+ function formatLocale(locale) {
200
+ var group = locale.grouping === undefined || locale.thousands === undefined ? identity : formatGroup(map.call(locale.grouping, Number), locale.thousands + ""),
201
+ currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
202
+ currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
203
+ decimal = locale.decimal === undefined ? "." : locale.decimal + "",
204
+ numerals = locale.numerals === undefined ? identity : formatNumerals(map.call(locale.numerals, String)),
205
+ percent = locale.percent === undefined ? "%" : locale.percent + "",
206
+ minus = locale.minus === undefined ? "−" : locale.minus + "",
207
+ nan = locale.nan === undefined ? "NaN" : locale.nan + "";
208
+
209
+ function newFormat(specifier) {
210
+ specifier = formatSpecifier(specifier);
211
+
212
+ var fill = specifier.fill,
213
+ align = specifier.align,
214
+ sign = specifier.sign,
215
+ symbol = specifier.symbol,
216
+ zero = specifier.zero,
217
+ width = specifier.width,
218
+ comma = specifier.comma,
219
+ precision = specifier.precision,
220
+ trim = specifier.trim,
221
+ type = specifier.type;
222
+
223
+ // The "n" type is an alias for ",g".
224
+ if (type === "n") comma = true, type = "g";
225
+
226
+ // The "" type, and any invalid type, is an alias for ".12~g".
227
+ else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g";
228
+
229
+ // If zero fill is specified, padding goes after sign and before digits.
230
+ if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "=";
231
+
232
+ // Compute the prefix and suffix.
233
+ // For SI-prefix, the suffix is lazily computed.
234
+ var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
235
+ suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : "";
236
+
237
+ // What format function should we use?
238
+ // Is this an integer type?
239
+ // Can this type generate exponential notation?
240
+ var formatType = formatTypes[type],
241
+ maybeSuffix = /[defgprs%]/.test(type);
242
+
243
+ // Set the default precision if not specified,
244
+ // or clamp the specified precision to the supported range.
245
+ // For significant precision, it must be in [1, 21].
246
+ // For fixed precision, it must be in [0, 20].
247
+ precision = precision === undefined ? 6
248
+ : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))
249
+ : Math.max(0, Math.min(20, precision));
250
+
251
+ function format(value) {
252
+ var valuePrefix = prefix,
253
+ valueSuffix = suffix,
254
+ i, n, c;
255
+
256
+ if (type === "c") {
257
+ valueSuffix = formatType(value) + valueSuffix;
258
+ value = "";
259
+ } else {
260
+ value = +value;
261
+
262
+ // Determine the sign. -0 is not less than 0, but 1 / -0 is!
263
+ var valueNegative = value < 0 || 1 / value < 0;
264
+
265
+ // Perform the initial formatting.
266
+ value = isNaN(value) ? nan : formatType(Math.abs(value), precision);
267
+
268
+ // Trim insignificant zeros.
269
+ if (trim) value = formatTrim(value);
270
+
271
+ // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
272
+ if (valueNegative && +value === 0 && sign !== "+") valueNegative = false;
273
+
274
+ // Compute the prefix and suffix.
275
+ valuePrefix = (valueNegative ? (sign === "(" ? sign : minus) : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
276
+ valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : "");
277
+
278
+ // Break the formatted value into the integer “value” part that can be
279
+ // grouped, and fractional or exponential “suffix” part that is not.
280
+ if (maybeSuffix) {
281
+ i = -1, n = value.length;
282
+ while (++i < n) {
283
+ if (c = value.charCodeAt(i), 48 > c || c > 57) {
284
+ valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
285
+ value = value.slice(0, i);
286
+ break;
287
+ }
288
+ }
289
+ }
290
+ }
291
+
292
+ // If the fill character is not "0", grouping is applied before padding.
293
+ if (comma && !zero) value = group(value, Infinity);
294
+
295
+ // Compute the padding.
296
+ var length = valuePrefix.length + value.length + valueSuffix.length,
297
+ padding = length < width ? new Array(width - length + 1).join(fill) : "";
298
+
299
+ // If the fill character is "0", grouping is applied after padding.
300
+ if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = "";
301
+
302
+ // Reconstruct the final output based on the desired alignment.
303
+ switch (align) {
304
+ case "<": value = valuePrefix + value + valueSuffix + padding; break;
305
+ case "=": value = valuePrefix + padding + value + valueSuffix; break;
306
+ case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;
307
+ default: value = padding + valuePrefix + value + valueSuffix; break;
308
+ }
309
+
310
+ return numerals(value);
311
+ }
312
+
313
+ format.toString = function() {
314
+ return specifier + "";
315
+ };
316
+
317
+ return format;
318
+ }
319
+
320
+ function formatPrefix(specifier, value) {
321
+ var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
322
+ e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
323
+ k = Math.pow(10, -e),
324
+ prefix = prefixes[8 + e / 3];
325
+ return function(value) {
326
+ return f(k * value) + prefix;
327
+ };
328
+ }
329
+
330
+ return {
331
+ format: newFormat,
332
+ formatPrefix: formatPrefix
333
+ };
334
+ }
335
+
336
+ var locale;
337
+ var format;
338
+
339
+ defaultLocale({
340
+ thousands: ",",
341
+ grouping: [3],
342
+ currency: ["$", ""]
343
+ });
344
+
345
+ function defaultLocale(definition) {
346
+ locale = formatLocale(definition);
347
+ format = locale.format;
348
+ locale.formatPrefix;
349
+ return locale;
350
+ }
351
+
39
352
  // Utility functions for our components
40
353
  var ONEAVGMONTH = 2629800000; // Average month length in ms, copied from plotly constants in plotly.js/src/constants/numerical.js
41
354
  // Type guard to check if array contains only numbers
@@ -6052,11 +6365,11 @@ var Loading = function () {
6052
6365
  var Plot$2 = lazy(function () { return import('react-plotly.js'); });
6053
6366
  var HistogramPlot = function (props) {
6054
6367
  var _a, _b, _c, _d;
6055
- var data = props.data, title = props.title, xAxisTitle = props.xAxisTitle, _e = props.barColor, barColor = _e === void 0 ? "rgb(72, 72, 74)" : _e, _f = props.unselectedBarColor, unselectedBarColor = _f === void 0 ? "rgba(203, 195, 195, 0.88)" : _f, _g = props.selectorsColor, selectorsColor = _g === void 0 ? "black" : _g, containerStyleOverrides = props.containerStyleOverrides, _h = props.unselectedData, unselectedData = _h === void 0 ? [] : _h, _j = props.handleClickOrSelection, handleClickOrSelection = _j === void 0 ? function () { } : _j, _k = props.onDeselect, onDeselect = _k === void 0 ? function () { } : _k, plotId = props.plotId, _l = props.selectByBin, selectByBin = _l === void 0 ? false : _l, dateTickFormat = props.dateTickFormat, binSizeOverride = props.binSizeOverride, _m = props.statsAnnotations, statsAnnotations = _m === void 0 ? ["mean"] : _m, _o = props.emptySelectedRange, emptySelectedRange = _o === void 0 ? false : _o;
6368
+ var data = props.data, title = props.title, xAxisTitle = props.xAxisTitle, _e = props.barColor, barColor = _e === void 0 ? "rgb(72, 72, 74)" : _e, _f = props.unselectedBarColor, unselectedBarColor = _f === void 0 ? "rgba(203, 195, 195, 0.88)" : _f, _g = props.selectorsColor, selectorsColor = _g === void 0 ? "black" : _g, containerStyleOverrides = props.containerStyleOverrides, _h = props.unselectedData, unselectedData = _h === void 0 ? [] : _h, _j = props.handleClickOrSelection, handleClickOrSelection = _j === void 0 ? function () { } : _j, _k = props.onDeselect, onDeselect = _k === void 0 ? function () { } : _k, plotId = props.plotId, _l = props.selectByBin, selectByBin = _l === void 0 ? false : _l, dateTickFormat = props.dateTickFormat, binSizeOverride = props.binSizeOverride, _m = props.statsAnnotations, statsAnnotations = _m === void 0 ? ["mean"] : _m, _o = props.emptySelectedRange, emptySelectedRange = _o === void 0 ? false : _o, _p = props.d3FormatValueString, d3FormatValueString = _p === void 0 ? ".1f" : _p;
6056
6369
  // Ref for plot container
6057
6370
  var containerRef = useRef(null);
6058
6371
  // Track any selections made in this plot so we can style the selection box.
6059
- var _p = useState(null), selectedRange = _p[0], setSelectedRange = _p[1];
6372
+ var _q = useState(null), selectedRange = _q[0], setSelectedRange = _q[1];
6060
6373
  // If the parent component wants to clear the selection, reset selectedRange to null.
6061
6374
  useEffect(function () {
6062
6375
  if (emptySelectedRange) {
@@ -6078,9 +6391,9 @@ var HistogramPlot = function (props) {
6078
6391
  // Plotly determines "nice" bins which consequently means it internally decides the axis range. We need
6079
6392
  // to access that information once the plot has been initialized so that we can prevent the
6080
6393
  // axis range from changing during interaction. Dates use strings.
6081
- var _q = useState(undefined), fixedXAxisRange = _q[0], setFixedXAxisRange = _q[1];
6394
+ var _r = useState(undefined), fixedXAxisRange = _r[0], setFixedXAxisRange = _r[1];
6082
6395
  // track xbins too
6083
- var _r = useState(undefined), binSize = _r[0], setBinSize = _r[1];
6396
+ var _s = useState(undefined), binSize = _s[0], setBinSize = _s[1];
6084
6397
  // Once the plot is drawn, record the initial axis range so we can keep it fixed.
6085
6398
  // figure should be Readonly<Plotly.Figure> but react-plotly.js doesn't expose that type, so we use any.
6086
6399
  var handlePlotUpdate = function (figure, graphDiv) {
@@ -6466,7 +6779,7 @@ var HistogramPlot = function (props) {
6466
6779
  day: "2-digit",
6467
6780
  year: "2-digit",
6468
6781
  })
6469
- : meanValue.toFixed(2), "</span>"),
6782
+ : format(d3FormatValueString)(meanValue), "</span>"),
6470
6783
  xanchor: "center",
6471
6784
  yanchor: "bottom",
6472
6785
  showarrow: false,
@@ -6491,7 +6804,7 @@ var HistogramPlot = function (props) {
6491
6804
  day: "2-digit",
6492
6805
  year: "2-digit",
6493
6806
  })
6494
- : stdevValue.toFixed(2), "</span>"),
6807
+ : format(d3FormatValueString)(stdevValue), "</span>"),
6495
6808
  xanchor: "center",
6496
6809
  yanchor: "bottom",
6497
6810
  showarrow: false,
@@ -6555,12 +6868,9 @@ var HistogramPlot = function (props) {
6555
6868
  linewidth: 1,
6556
6869
  fixedrange: true, // Disable zooming
6557
6870
  ticklabelposition: "outside",
6558
- tickformat: isDateArray(data) ? dateTickFormat : undefined, // Format ticks for dates
6871
+ tickformat: isDateArray(data) ? dateTickFormat : d3FormatValueString, // Format ticks for dates
6559
6872
  automargin: true, // Adjust margin if tick labels rotate
6560
- hoverformat: isNumberArray(allData) &&
6561
- Math.max.apply(Math, allData) - Math.min.apply(Math, allData) > 3
6562
- ? ".1~f"
6563
- : undefined,
6873
+ hoverformat: isNumberArray(allData) ? d3FormatValueString : undefined,
6564
6874
  },
6565
6875
  yaxis: {
6566
6876
  title: {