@sybilion/uilib 1.2.26 → 1.3.1

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.
Files changed (137) hide show
  1. package/dist/esm/components/ui/Chart/Chart.js +5 -0
  2. package/dist/esm/components/ui/Chart/components/BaseChartWrapper.js +7 -32
  3. package/dist/esm/components/ui/Chart/components/ChartEmptyState/ChartEmptyState.js +21 -0
  4. package/dist/esm/components/ui/Chart/components/ChartEmptyState/ChartEmptyState.styl.js +7 -0
  5. package/dist/esm/components/ui/Chart/lightweight/LightweightForecastChart.js +460 -0
  6. package/dist/esm/components/ui/Chart/lightweight/LightweightForecastChart.styl.js +7 -0
  7. package/dist/esm/components/ui/Chart/lightweight/chartTime.js +16 -0
  8. package/dist/esm/components/ui/Chart/lightweight/lightweightForecastChart.helpers.js +114 -0
  9. package/dist/esm/components/ui/Chart/lightweight/quantileBandCustomSeries.js +147 -0
  10. package/dist/esm/components/ui/Chart/quantileBandConeChartData.js +131 -0
  11. package/dist/esm/components/ui/Chart/tools/chartPlotGeometry.js +65 -0
  12. package/dist/esm/components/ui/ChartAreaInteractive/ChartAreaInteractive.helpers.js +37 -1
  13. package/dist/esm/components/ui/ChartAreaInteractive/ChartAreaInteractive.js +5 -2
  14. package/dist/esm/components/ui/ChartAreaInteractive/TimeRangeBrushLayer.js +205 -0
  15. package/dist/esm/components/ui/ChartAreaInteractive/TimeRangeBrushLayer.styl.js +7 -0
  16. package/dist/esm/components/ui/ChartAreaInteractive/TimeRangeBrushLayout.helpers.js +37 -0
  17. package/dist/esm/components/ui/ChartAreaInteractive/overlays/IntervalsOverlay/IntervalsOverlay.hooks.js +1 -0
  18. package/dist/esm/components/ui/ChartAreaInteractive/overlays/PinOverlay/PinOverlay.js +7 -60
  19. package/dist/esm/components/ui/ChartAreaInteractive/overlays/PinOverlay/PinOverlay.styl.js +2 -2
  20. package/dist/esm/components/ui/ChartAreaInteractive/overlays/ThresholdsOverlay/ThresholdsOverlay.hooks.js +1 -0
  21. package/dist/esm/components/ui/ChartAreaInteractive/overlays/useChartYRange.js +2 -4
  22. package/dist/esm/components/ui/ChartAreaInteractive/overlays/useQuantileBands.js +4 -102
  23. package/dist/esm/components/ui/TimeRangeControls/TimeRangeControls.js +7 -2
  24. package/dist/esm/components/ui/WorldMap/WorldMap.js +11 -0
  25. package/dist/esm/components/ui/WorldMap/WorldMap.styl.js +7 -0
  26. package/dist/esm/components/widgets/DriverCard/DriverCard.js +89 -0
  27. package/dist/esm/components/widgets/DriverCard/DriverCard.styl.js +7 -0
  28. package/dist/esm/components/widgets/DriverCard/DriverPerformanceChart.js +83 -0
  29. package/dist/esm/components/widgets/DriverCard/DriverPerformanceChart.styl.js +7 -0
  30. package/dist/esm/components/widgets/DriverCard/driverPerformanceChartData.js +50 -0
  31. package/dist/esm/components/widgets/DriverMap/DriverMap.js +2 -2
  32. package/dist/esm/components/widgets/DriverMap/DriverMap.styl.js +2 -2
  33. package/dist/esm/index.js +4 -2
  34. package/dist/esm/types/src/components/ui/Chart/Chart.d.ts +2 -0
  35. package/dist/esm/types/src/components/ui/Chart/components/BaseChartWrapper.d.ts +2 -1
  36. package/dist/esm/types/src/components/ui/Chart/components/ChartEmptyState/ChartEmptyState.d.ts +14 -0
  37. package/dist/esm/types/src/components/ui/Chart/lightweight/LightweightForecastChart.d.ts +26 -0
  38. package/dist/esm/types/src/components/ui/Chart/lightweight/chartTime.d.ts +5 -0
  39. package/dist/esm/types/src/components/ui/Chart/lightweight/lightweightForecastChart.helpers.d.ts +13 -0
  40. package/dist/esm/types/src/components/ui/Chart/lightweight/quantileBandCustomSeries.d.ts +24 -0
  41. package/dist/esm/types/src/components/ui/Chart/quantileBandConeChartData.d.ts +7 -0
  42. package/dist/esm/types/src/components/ui/Chart/tools/chartPlotGeometry.d.ts +30 -0
  43. package/dist/esm/types/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.d.ts +1 -1
  44. package/dist/esm/types/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.helpers.d.ts +11 -2
  45. package/dist/esm/types/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.types.d.ts +2 -2
  46. package/dist/esm/types/src/components/ui/ChartAreaInteractive/TimeRangeBrushLayer.d.ts +15 -0
  47. package/dist/esm/types/src/components/ui/ChartAreaInteractive/TimeRangeBrushLayout.helpers.d.ts +14 -0
  48. package/dist/esm/types/src/components/ui/ChartAreaInteractive/overlays/PinOverlay/PinOverlay.d.ts +1 -1
  49. package/dist/esm/types/src/components/ui/Page/PageColumns/PageColumns.d.ts +1 -1
  50. package/dist/esm/types/src/components/ui/TimeRangeControls/TimeRangeControls.d.ts +5 -7
  51. package/dist/esm/types/src/components/ui/WorldMap/WorldMap.d.ts +4 -0
  52. package/dist/esm/types/src/components/ui/WorldMap/index.d.ts +2 -0
  53. package/dist/esm/types/src/components/widgets/DriverCard/DriverCard.d.ts +9 -0
  54. package/dist/esm/types/src/components/widgets/DriverCard/DriverPerformanceChart.d.ts +5 -0
  55. package/dist/esm/types/src/components/widgets/DriverCard/driverPerformanceChartData.d.ts +7 -0
  56. package/dist/esm/types/src/components/widgets/DriverCard/index.d.ts +1 -0
  57. package/dist/esm/types/src/components/widgets/DriverMap/index.d.ts +0 -2
  58. package/dist/esm/types/src/docs/pages/LightweightChartPage.d.ts +1 -0
  59. package/dist/esm/types/src/docs/pages/PageColumnsPage.d.ts +1 -0
  60. package/dist/esm/types/src/docs/pages/WorldMapPage.d.ts +1 -0
  61. package/dist/esm/types/src/index.d.ts +2 -0
  62. package/package.json +3 -2
  63. package/src/components/ui/Chart/Chart.tsx +9 -0
  64. package/src/components/ui/Chart/components/BaseChartWrapper.tsx +8 -41
  65. package/src/components/ui/Chart/components/ChartEmptyState/ChartEmptyState.styl +60 -0
  66. package/src/components/ui/Chart/components/ChartEmptyState/ChartEmptyState.styl.d.ts +15 -0
  67. package/src/components/ui/Chart/components/ChartEmptyState/ChartEmptyState.tsx +66 -0
  68. package/src/components/ui/Chart/lightweight/LightweightForecastChart.styl +25 -0
  69. package/src/components/ui/Chart/lightweight/LightweightForecastChart.styl.d.ts +11 -0
  70. package/src/components/ui/Chart/lightweight/LightweightForecastChart.tsx +721 -0
  71. package/src/components/ui/Chart/lightweight/chartTime.ts +18 -0
  72. package/src/components/ui/Chart/lightweight/lightweightForecastChart.helpers.ts +141 -0
  73. package/src/components/ui/Chart/lightweight/quantileBandCustomSeries.ts +215 -0
  74. package/src/components/ui/Chart/quantileBandConeChartData.ts +171 -0
  75. package/src/components/ui/Chart/tools/chartPlotGeometry.ts +89 -0
  76. package/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.helpers.ts +44 -2
  77. package/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.tsx +14 -1
  78. package/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.types.ts +2 -3
  79. package/src/components/ui/ChartAreaInteractive/TimeRangeBrushLayer.styl +21 -0
  80. package/src/components/{widgets/DriverMap/LoadingSpinner/LoadingSpinner.styl.d.ts → ui/ChartAreaInteractive/TimeRangeBrushLayer.styl.d.ts} +3 -3
  81. package/src/components/ui/ChartAreaInteractive/TimeRangeBrushLayer.tsx +285 -0
  82. package/src/components/ui/ChartAreaInteractive/TimeRangeBrushLayout.helpers.ts +55 -0
  83. package/src/components/ui/ChartAreaInteractive/overlays/IntervalsOverlay/IntervalsOverlay.hooks.ts +1 -0
  84. package/src/components/ui/ChartAreaInteractive/overlays/PinOverlay/PinOverlay.styl +2 -7
  85. package/src/components/ui/ChartAreaInteractive/overlays/PinOverlay/PinOverlay.styl.d.ts +0 -1
  86. package/src/components/ui/ChartAreaInteractive/overlays/PinOverlay/PinOverlay.tsx +7 -71
  87. package/src/components/ui/ChartAreaInteractive/overlays/ThresholdsOverlay/ThresholdsOverlay.hooks.ts +1 -0
  88. package/src/components/ui/ChartAreaInteractive/overlays/useChartYRange.ts +2 -3
  89. package/src/components/ui/ChartAreaInteractive/overlays/useQuantileBands.ts +5 -131
  90. package/src/components/ui/Page/PageColumns/PageColumns.tsx +1 -1
  91. package/src/components/ui/TimeRangeControls/TimeRangeControls.tsx +16 -17
  92. package/src/components/{widgets/DriverMap/MapBackground/MapBackground.styl → ui/WorldMap/WorldMap.styl} +1 -3
  93. package/src/components/{widgets/DriverMap/MapBackground/MapBackground.styl.d.ts → ui/WorldMap/WorldMap.styl.d.ts} +1 -1
  94. package/src/components/ui/WorldMap/WorldMap.tsx +22 -0
  95. package/src/components/ui/WorldMap/index.ts +2 -0
  96. package/src/components/widgets/DriverCard/DriverCard.styl +169 -0
  97. package/src/components/widgets/DriverCard/DriverCard.styl.d.ts +40 -0
  98. package/src/components/widgets/DriverCard/DriverCard.tsx +219 -0
  99. package/src/components/widgets/DriverCard/DriverPerformanceChart.styl +43 -0
  100. package/src/components/widgets/DriverCard/DriverPerformanceChart.styl.d.ts +13 -0
  101. package/src/components/widgets/DriverCard/DriverPerformanceChart.tsx +150 -0
  102. package/src/components/widgets/DriverCard/driverPerformanceChartData.ts +64 -0
  103. package/src/components/widgets/DriverCard/index.ts +1 -0
  104. package/src/components/widgets/DriverMap/DriverIcon/DriverIcon.tsx +0 -2
  105. package/src/components/widgets/DriverMap/DriverMap.styl +6 -1
  106. package/src/components/widgets/DriverMap/DriverMap.styl.d.ts +1 -0
  107. package/src/components/widgets/DriverMap/DriverMap.tsx +2 -4
  108. package/src/components/widgets/DriverMap/driverCategoryIcon.tsx +0 -2
  109. package/src/components/widgets/DriverMap/index.ts +0 -2
  110. package/src/declarations.d.ts +2 -0
  111. package/src/docs/config/webpack.config.js +26 -3
  112. package/src/docs/index.tsx +1 -1
  113. package/src/docs/pages/ChartAreaInteractivePage.tsx +2 -3
  114. package/src/docs/pages/DriverMapPage.tsx +214 -60
  115. package/src/docs/pages/LightweightChartPage.styl +18 -0
  116. package/src/docs/pages/LightweightChartPage.styl.d.ts +10 -0
  117. package/src/docs/pages/LightweightChartPage.tsx +195 -0
  118. package/src/docs/pages/PageColumnsPage.tsx +92 -0
  119. package/src/docs/pages/TimeRangeControlsPage.tsx +2 -3
  120. package/src/docs/pages/WorldMapPage.styl +14 -0
  121. package/src/docs/pages/WorldMapPage.styl.d.ts +8 -0
  122. package/src/docs/pages/WorldMapPage.tsx +26 -0
  123. package/src/docs/registry.ts +19 -1
  124. package/src/index.ts +2 -0
  125. package/dist/esm/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.js +0 -8
  126. package/dist/esm/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.styl.js +0 -7
  127. package/dist/esm/components/widgets/DriverMap/MapBackground/MapBackground.js +0 -10
  128. package/dist/esm/components/widgets/DriverMap/MapBackground/MapBackground.styl.js +0 -7
  129. package/dist/esm/types/src/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.d.ts +0 -1
  130. package/dist/esm/types/src/components/widgets/DriverMap/MapBackground/MapBackground.d.ts +0 -1
  131. package/src/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.styl +0 -24
  132. package/src/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.tsx +0 -11
  133. package/src/components/widgets/DriverMap/MapBackground/MapBackground.tsx +0 -18
  134. /package/dist/esm/components/{widgets/DriverMap/MapBackground → ui/WorldMap}/map.svg.js +0 -0
  135. /package/src/components/{widgets/DriverMap/MapBackground → ui/WorldMap}/map.svg +0 -0
  136. /package/src/components/{widgets/DriverMap/MapBackground → ui/WorldMap}/mapAspect.mixin.styl +0 -0
  137. /package/src/components/{widgets/DriverMap/MapBackground → ui/WorldMap}/mapAspect.mixin.styl.d.ts +0 -0
@@ -16,6 +16,11 @@ import '../TextShimmer/TextShimmer.js';
16
16
  import '@phosphor-icons/react';
17
17
  import '../AnalysesSelector/AnalysesSelector.styl.js';
18
18
  import './components/CustomChartLegend/CustomChartLegend.styl.js';
19
+ import '../ChartAreaInteractive/ChartLines.js';
20
+ import '../Skeleton/Skeleton.styl.js';
21
+ import 'lightweight-charts';
22
+ import './lightweight/LightweightForecastChart.styl.js';
23
+ import './components/ChartEmptyState/ChartEmptyState.styl.js';
19
24
 
20
25
  const ChartTooltip = RechartsPrimitive.Tooltip;
21
26
  const ChartLegend = RechartsPrimitive.Legend;
@@ -1,46 +1,21 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import cn from 'classnames';
3
3
  import { forwardRef, useState, useRef, useMemo, useEffect } from 'react';
4
- import { ChartTooltip } from '../Chart.js';
5
- import { QuantileBands } from './QuantileBands.js';
6
4
  import { getForecastColor, ChartLines } from '../../ChartAreaInteractive/ChartLines.js';
7
5
  import { Skeleton } from '../../Skeleton/Skeleton.js';
8
6
  import { chartRenderQueue } from '../../../../utils/chartRenderQueue.js';
9
- import { LineChart, ComposedChart } from 'recharts';
7
+ import { Tooltip, LineChart, ComposedChart } from 'recharts';
8
+ import { ChartContainer } from './ChartContainer.js';
9
+ import { ChartTooltipContent } from './ChartTooltipContent.js';
10
+ import { CustomChartLegend } from './CustomChartLegend/CustomChartLegend.js';
11
+ import { QuantileBands } from './QuantileBands.js';
12
+ import { resolveChartMargin, getPlotViewBox } from '../tools/chartPlotGeometry.js';
10
13
  import { formatDate } from '../tools/formatters.js';
11
14
  import S from './BaseChartWrapper.styl.js';
12
15
  import { ChartAxes } from './ChartAxes.js';
13
16
  import { ChartGrid } from './ChartGrid.js';
14
17
  import { LegendSvg } from './LegendSvg/LegendSvg.js';
15
- import { ChartContainer } from './ChartContainer.js';
16
- import { CustomChartLegend } from './CustomChartLegend/CustomChartLegend.js';
17
- import { ChartTooltipContent } from './ChartTooltipContent.js';
18
18
 
19
- const DEFAULT_CHART_MARGIN = {
20
- top: 5,
21
- right: 5,
22
- bottom: 5,
23
- left: 5,
24
- };
25
- function resolveChartMargin(margin) {
26
- return {
27
- top: margin?.top ?? DEFAULT_CHART_MARGIN.top,
28
- right: margin?.right ?? DEFAULT_CHART_MARGIN.right,
29
- bottom: margin?.bottom ?? DEFAULT_CHART_MARGIN.bottom,
30
- left: margin?.left ?? DEFAULT_CHART_MARGIN.left,
31
- };
32
- }
33
- /** Plot box inside `.recharts-wrapper`, same convention as Recharts cartesian viewBox. */
34
- function getPlotViewBox(wrapper, m) {
35
- const w = wrapper.clientWidth;
36
- const h = wrapper.clientHeight;
37
- return {
38
- x: m.left,
39
- y: m.top,
40
- width: Math.max(0, w - m.left - m.right),
41
- height: Math.max(0, h - m.top - m.bottom),
42
- };
43
- }
44
19
  function clampTooltipTranslate(args) {
45
20
  const { coordinate, viewBox, tooltipWidth: tw, tooltipHeight: th, offset, edgeMargin, } = args;
46
21
  const minX = viewBox.x + edgeMargin;
@@ -332,7 +307,7 @@ const BaseChartWrapperContent = forwardRef((props, ref) => {
332
307
  }
333
308
  const ChartComponent = chartType === 'line' ? LineChart : ComposedChart;
334
309
  const defaultLabelFormatter = (v) => formatDateFn(v, true);
335
- return (jsxs("div", { className: cn(S.root, !showLegend && S.noLegend, !showChartAxesLegend && S.hideChartAxesLegend, isLoaded && S.loaded, className), ref: setRefs, children: [loading && (jsx("div", { className: S.loadingOverlay, children: jsx(Skeleton, {}) })), showGrid && (jsx(ChartContainer, { config: chartConfig, className: cn(S.gridLayer, chartClassName), style: height ? { height: `${height}px` } : undefined, children: jsxs(ChartComponent, { data: chartData, margin: margin, children: [jsx(ChartGrid, {}), showAxes && (jsx(ChartAxes, { formatDate: formatDateFn, formatNumber: formatNumber, xAxisClassName: xAxisClassName, yAxisClassName: yAxisClassName, xAxisLabel: xAxisLabel, yAxisLabel: yAxisLabel, xMin: xMin, xMax: xMax, yMin: yMin, yMax: yMax, autoScaleYAxis: effectiveAutoScale }))] }) })), jsx(ChartContainer, { config: chartConfig, className: cn(S.chartLayer, chartClassName), style: height ? { height: `${height}px` } : undefined, ...containerProps, children: jsxs(ChartComponent, { data: chartData, margin: margin, children: [showAxes && (jsx(ChartAxes, { formatDate: formatDateFn, formatNumber: formatNumber, xAxisClassName: cn(xAxisClassName), yAxisClassName: cn(yAxisClassName), xAxisLabel: xAxisLabel, yAxisLabel: yAxisLabel, xMin: xMin, xMax: xMax, yMin: yMin, yMax: yMax, autoScaleYAxis: effectiveAutoScale })), quantileBands?.[0] && (jsx(QuantileBands, { hiddenBands: hiddenSeries, quantileBandKey: quantileBandKey, animate: true, animationDuration: 150, animationBegin: 0, customBands: quantileBands, showLegend: showLegend })), jsx(ChartLines, { historicalLineColor: historicalLineColor, chartData: chartData, forecastData: forecastData, hiddenSeries: hiddenSeries, isDarkTheme: isDarkTheme, shouldAnimate: shouldAnimate, showLegend: showLegend, forecastLineStyle: forecastLineStyle }), showTooltip && (jsx("div", { children: jsx(ChartTooltip, { cursor: false, offset: TOOLTIP_OFFSET, allowEscapeViewBox: { x: false, y: false }, content: renderTooltipContent }) }))] }) }), overlayElements, jsxs("div", { className: cn(S.footer, footerClassName), children: [showLegend &&
310
+ return (jsxs("div", { className: cn(S.root, !showLegend && S.noLegend, !showChartAxesLegend && S.hideChartAxesLegend, isLoaded && S.loaded, className), ref: setRefs, children: [loading && (jsx("div", { className: S.loadingOverlay, children: jsx(Skeleton, {}) })), showGrid && (jsx(ChartContainer, { config: chartConfig, className: cn(S.gridLayer, chartClassName), style: height ? { height: `${height}px` } : undefined, children: jsxs(ChartComponent, { data: chartData, margin: margin, children: [jsx(ChartGrid, {}), showAxes && (jsx(ChartAxes, { formatDate: formatDateFn, formatNumber: formatNumber, xAxisClassName: xAxisClassName, yAxisClassName: yAxisClassName, xAxisLabel: xAxisLabel, yAxisLabel: yAxisLabel, xMin: xMin, xMax: xMax, yMin: yMin, yMax: yMax, autoScaleYAxis: effectiveAutoScale }))] }) })), jsx(ChartContainer, { config: chartConfig, className: cn(S.chartLayer, chartClassName), style: height ? { height: `${height}px` } : undefined, ...containerProps, children: jsxs(ChartComponent, { data: chartData, margin: margin, children: [showAxes && (jsx(ChartAxes, { formatDate: formatDateFn, formatNumber: formatNumber, xAxisClassName: cn(xAxisClassName), yAxisClassName: cn(yAxisClassName), xAxisLabel: xAxisLabel, yAxisLabel: yAxisLabel, xMin: xMin, xMax: xMax, yMin: yMin, yMax: yMax, autoScaleYAxis: effectiveAutoScale })), quantileBands?.[0] && (jsx(QuantileBands, { hiddenBands: hiddenSeries, quantileBandKey: quantileBandKey, animate: true, animationDuration: 150, animationBegin: 0, customBands: quantileBands, showLegend: showLegend })), jsx(ChartLines, { historicalLineColor: historicalLineColor, chartData: chartData, forecastData: forecastData, hiddenSeries: hiddenSeries, isDarkTheme: isDarkTheme, shouldAnimate: shouldAnimate, showLegend: showLegend, forecastLineStyle: forecastLineStyle }), showTooltip && (jsx("div", { children: jsx(Tooltip, { cursor: false, offset: TOOLTIP_OFFSET, allowEscapeViewBox: { x: false, y: false }, content: renderTooltipContent }) }))] }) }), overlayElements, jsxs("div", { className: cn(S.footer, footerClassName), children: [showLegend &&
336
311
  (legendVariant === 'svg' ? (jsx(LegendSvg, { payload: legendPayload.map(p => ({
337
312
  value: p.value,
338
313
  color: p.color,
@@ -0,0 +1,21 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import cn from 'classnames';
3
+ import S from './ChartEmptyState.styl.js';
4
+
5
+ function isNonEmpty(node) {
6
+ if (node == null || node === false)
7
+ return false;
8
+ if (typeof node === 'string')
9
+ return node.trim().length > 0;
10
+ return true;
11
+ }
12
+ function ChartEmptyState({ className, hint, status, statusTone = 'muted', variant = 'panel', align = 'center', }) {
13
+ const hasHint = isNonEmpty(hint);
14
+ const hasStatus = isNonEmpty(status);
15
+ if (!hasHint && !hasStatus) {
16
+ return null;
17
+ }
18
+ return (jsxs("div", { className: cn(S.root, variant === 'panel' && S.rootPanel, variant === 'inline' && S.rootInline, align === 'start' && S.rootAlignStart, align === 'center' && S.rootAlignCenter, className), children: [hasHint && jsx("div", { className: S.hint, children: hint }), hasStatus && (jsx("div", { className: cn(S.status, statusTone === 'destructive' && S.statusDestructive, statusTone === 'muted' && S.statusMuted), children: status }))] }));
19
+ }
20
+
21
+ export { ChartEmptyState };
@@ -0,0 +1,7 @@
1
+ import styleInject from 'style-inject';
2
+
3
+ var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.ChartEmptyState_root__-Dzme{backdrop-filter:blur(4px);border-radius:var(--p-4);box-shadow:0 0 0 2px var(--page-color);display:flex;flex-direction:column;gap:var(--p-1);max-width:42rem;padding:0 var(--p-3);pointer-events:auto}.ChartEmptyState_rootPanel__hiHyM{background:var(--muted)/.35;border-radius:var(--radius-md);justify-content:center;min-height:var(--chart-height);padding:var(--p-6);width:100%}.ChartEmptyState_rootInline__NV7yu{max-width:100%;width:-moz-fit-content;width:fit-content}.ChartEmptyState_rootAlignCenter__9LySU{align-items:center;text-align:center}.ChartEmptyState_rootAlignStart__fA7lc{align-items:flex-start;text-align:left}.ChartEmptyState_hint__9PSr1{color:var(--muted-foreground);font-size:14px;font-weight:400;line-height:1.5;margin:0}.ChartEmptyState_hint__9PSr1 a{color:var(--sb-cyan-400);font-weight:500;text-decoration:underline}.ChartEmptyState_hint__9PSr1 a:hover{opacity:.9}.ChartEmptyState_status__sR1qq{font-size:14px;font-weight:400;line-height:1.5;margin:0}.ChartEmptyState_statusMuted__h0nek{color:var(--muted-foreground)}.ChartEmptyState_statusDestructive__cMCuF{color:var(--destructive)}";
4
+ var S = {"root":"ChartEmptyState_root__-Dzme","rootPanel":"ChartEmptyState_rootPanel__hiHyM","rootInline":"ChartEmptyState_rootInline__NV7yu","rootAlignCenter":"ChartEmptyState_rootAlignCenter__9LySU","rootAlignStart":"ChartEmptyState_rootAlignStart__fA7lc","hint":"ChartEmptyState_hint__9PSr1","status":"ChartEmptyState_status__sR1qq","statusMuted":"ChartEmptyState_statusMuted__h0nek","statusDestructive":"ChartEmptyState_statusDestructive__cMCuF"};
5
+ styleInject(css_248z);
6
+
7
+ export { S as default };
@@ -0,0 +1,460 @@
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import cn from 'classnames';
3
+ import { useId, useRef, useState, useMemo, useEffect, useCallback } from 'react';
4
+ import { ChartContext } from '../Chart.context.js';
5
+ import { ChartStyle } from '../components/ChartContainer.js';
6
+ import { ChartTooltipContent } from '../components/ChartTooltipContent.js';
7
+ import { CustomChartLegend } from '../components/CustomChartLegend/CustomChartLegend.js';
8
+ import { formatDate } from '../tools/formatters.js';
9
+ import { getForecastColor, getForecastQuantileBandColor } from '../../ChartAreaInteractive/ChartLines.js';
10
+ import { Skeleton } from '../../Skeleton/Skeleton.js';
11
+ import { ensureChartForecastBridge } from '../../../../utils/chartConnectionPoint.js';
12
+ import { createChart, LineSeries, LineType, LineStyle } from 'lightweight-charts';
13
+ import S from './LightweightForecastChart.styl.js';
14
+ import { buildLightweightChartOptions, buildHistoricalLineData, buildForecastLineData, buildQuantileBandCustomData, findNearestChartRow } from './lightweightForecastChart.helpers.js';
15
+ import { QuantileBandPaneView } from './quantileBandCustomSeries.js';
16
+
17
+ function clampTooltipTranslate(args) {
18
+ const { coordinate, viewW, viewH, tooltipWidth: tw, tooltipHeight: th, offset, edgeMargin, } = args;
19
+ const minX = edgeMargin;
20
+ const maxX = Math.max(edgeMargin, viewW - tw - edgeMargin);
21
+ const minY = edgeMargin;
22
+ const maxY = Math.max(edgeMargin, viewH - th - edgeMargin);
23
+ const clamp = (v, lo, hi) => Math.min(Math.max(v, lo), Math.max(lo, hi));
24
+ let tx = coordinate.x + offset;
25
+ if (tx + tw > viewW - edgeMargin) {
26
+ tx = coordinate.x - tw - offset;
27
+ }
28
+ tx = clamp(tx, minX, maxX);
29
+ let ty = coordinate.y + offset;
30
+ if (ty + th > viewH - edgeMargin) {
31
+ ty = coordinate.y - th - offset;
32
+ }
33
+ ty = clamp(ty, minY, maxY);
34
+ return { x: tx, y: ty };
35
+ }
36
+ function scheduleFitTimeScale(chart) {
37
+ requestAnimationFrame(() => {
38
+ chart.timeScale().fitContent();
39
+ });
40
+ }
41
+ function LightweightForecastChart(props) {
42
+ const { chartData, forecastData = [], quantileBands, chartConfig: chartConfigProp = {}, historicalLineColor: historicalLineColorProp, isDarkTheme, height, className, hiddenSeries: hiddenSeriesProp, onLegendClick, disableForecastHistoricalBridge = false, forecastLineStyle = 'dashed', formatDate: formatDateFn = formatDate, formatNumber, loading = false, error = null, noDataMessage = 'No data available', showLegend = true, showTooltip = true, } = props;
43
+ const chartId = useId().replace(/:/g, '');
44
+ const shellRef = useRef(null);
45
+ const hostRef = useRef(null);
46
+ const modelRef = useRef(null);
47
+ const bridgedRef = useRef([]);
48
+ const tooltipRef = useRef(null);
49
+ const tooltipSizeRef = useRef({ width: 0, height: 0 });
50
+ const [localHidden, setLocalHidden] = useState(() => new Set());
51
+ const hiddenSeries = hiddenSeriesProp ?? localHidden;
52
+ const hiddenControlled = hiddenSeriesProp !== undefined;
53
+ const pixelHeight = height ?? 280;
54
+ const historicalLineColor = historicalLineColorProp ?? (isDarkTheme ? '#ffffff' : '#000000');
55
+ const bridgedChartData = useMemo(() => {
56
+ if (disableForecastHistoricalBridge) {
57
+ return chartData;
58
+ }
59
+ return ensureChartForecastBridge(chartData, {
60
+ forecastSeriesIds: forecastData?.map(f => f.id),
61
+ });
62
+ }, [chartData, disableForecastHistoricalBridge, forecastData]);
63
+ bridgedRef.current = bridgedChartData;
64
+ const hiddenRef = useRef(hiddenSeries);
65
+ const forecastRef = useRef(forecastData);
66
+ const quantileBandsRef = useRef(quantileBands);
67
+ const formatDateRef = useRef(formatDateFn);
68
+ const histColorRef = useRef(historicalLineColor);
69
+ useEffect(() => {
70
+ hiddenRef.current = hiddenSeries;
71
+ }, [hiddenSeries]);
72
+ useEffect(() => {
73
+ forecastRef.current = forecastData;
74
+ }, [forecastData]);
75
+ useEffect(() => {
76
+ quantileBandsRef.current = quantileBands;
77
+ }, [quantileBands]);
78
+ useEffect(() => {
79
+ formatDateRef.current = formatDateFn;
80
+ }, [formatDateFn]);
81
+ useEffect(() => {
82
+ histColorRef.current = historicalLineColor;
83
+ }, [historicalLineColor]);
84
+ const mergedChartConfig = useMemo(() => {
85
+ const base = {
86
+ historical: { label: 'Historical Data', color: historicalLineColor },
87
+ ...chartConfigProp,
88
+ };
89
+ forecastData.forEach((f, index) => {
90
+ const key = `forecast_${f.id}`;
91
+ base[key] = {
92
+ label: f.name?.toString() ?? key,
93
+ color: f.color?.toString() ?? getForecastColor(index),
94
+ };
95
+ });
96
+ quantileBands?.forEach((band, index) => {
97
+ base[band.key] = {
98
+ label: band.name,
99
+ color: band.color ?? getForecastQuantileBandColor(index),
100
+ };
101
+ });
102
+ return base;
103
+ }, [chartConfigProp, forecastData, historicalLineColor, quantileBands]);
104
+ const legendPayload = useMemo(() => {
105
+ if (!showLegend)
106
+ return [];
107
+ return forecastData.map((item, index) => ({
108
+ value: item.name?.toString() || item.id?.toString() || '',
109
+ color: item.color?.toString() || getForecastColor(index),
110
+ dataKey: `forecast_${item.id}`,
111
+ icon: item.icon,
112
+ status: item.status,
113
+ dimmed: item.dimmed,
114
+ updated_at: item.updated_at,
115
+ }));
116
+ }, [forecastData, showLegend]);
117
+ const structureKey = useMemo(() => [
118
+ quantileBands?.map(b => b.key).join(',') ?? '',
119
+ forecastData.map(f => f.id).join(','),
120
+ ].join('|'), [forecastData, quantileBands]);
121
+ const [tooltipState, setTooltipState] = useState({
122
+ active: false,
123
+ x: 0,
124
+ y: 0,
125
+ label: '',
126
+ payload: [],
127
+ });
128
+ const applyTooltipPosition = useCallback(() => {
129
+ const shell = shellRef.current;
130
+ const wrapper = tooltipRef.current;
131
+ if (!shell || !wrapper)
132
+ return;
133
+ const tw = tooltipSizeRef.current.width || wrapper.offsetWidth;
134
+ const th = tooltipSizeRef.current.height || wrapper.offsetHeight;
135
+ if (tw <= 0 || th <= 0)
136
+ return;
137
+ const { width: viewW, height: viewH } = shell.getBoundingClientRect();
138
+ const next = clampTooltipTranslate({
139
+ coordinate: { x: tooltipState.x, y: tooltipState.y },
140
+ viewW,
141
+ viewH,
142
+ tooltipWidth: tw,
143
+ tooltipHeight: th,
144
+ offset: 10,
145
+ edgeMargin: 8,
146
+ });
147
+ wrapper.style.transform = `translate(${next.x}px, ${next.y}px)`;
148
+ }, [tooltipState.x, tooltipState.y]);
149
+ useEffect(() => {
150
+ if (!tooltipState.active)
151
+ return;
152
+ applyTooltipPosition();
153
+ }, [
154
+ applyTooltipPosition,
155
+ tooltipState.active,
156
+ tooltipState.payload,
157
+ tooltipState.label,
158
+ tooltipState.x,
159
+ tooltipState.y,
160
+ ]);
161
+ useEffect(() => {
162
+ const el = tooltipRef.current;
163
+ if (!el || typeof ResizeObserver === 'undefined')
164
+ return;
165
+ const ro = new ResizeObserver(entries => {
166
+ const entry = entries[0];
167
+ if (!entry)
168
+ return;
169
+ const { width, height: h } = entry.contentRect;
170
+ tooltipSizeRef.current = { width, height: h };
171
+ requestAnimationFrame(() => applyTooltipPosition());
172
+ });
173
+ ro.observe(el);
174
+ return () => ro.disconnect();
175
+ }, [applyTooltipPosition]);
176
+ useEffect(() => {
177
+ const onWin = () => requestAnimationFrame(() => {
178
+ applyTooltipPosition();
179
+ });
180
+ window.addEventListener('resize', onWin);
181
+ return () => window.removeEventListener('resize', onWin);
182
+ }, [applyTooltipPosition]);
183
+ // Structural chart lifecycle
184
+ useEffect(() => {
185
+ const host = hostRef.current;
186
+ if (!host)
187
+ return;
188
+ if (!bridgedChartData.length) {
189
+ modelRef.current?.chart.remove();
190
+ modelRef.current = null;
191
+ return;
192
+ }
193
+ const iw = Math.max(1, Math.floor(host.clientWidth || host.offsetWidth || 640));
194
+ const ih = Math.max(1, Math.floor(pixelHeight));
195
+ const chart = createChart(host, {
196
+ ...buildLightweightChartOptions({
197
+ isDarkTheme,
198
+ width: iw,
199
+ height: ih,
200
+ }),
201
+ });
202
+ const bands = new Map();
203
+ quantileBands?.forEach((band, index) => {
204
+ const fill = band.color ?? getForecastQuantileBandColor(index);
205
+ const view = new QuantileBandPaneView({
206
+ fill,
207
+ stroke: band.strokeWidth ? fill : undefined,
208
+ strokeWidth: band.strokeWidth ?? 0,
209
+ strokeDasharray: band.strokeDasharray,
210
+ strokeOpacity: band.strokeOpacity,
211
+ });
212
+ const api = chart.addCustomSeries(view, {
213
+ color: fill,
214
+ lastValueVisible: false,
215
+ priceLineVisible: false,
216
+ visible: !hiddenRef.current.has(band.key),
217
+ });
218
+ bands.set(band.key, { api, view });
219
+ });
220
+ const forecasts = new Map();
221
+ forecastData.forEach((f, index) => {
222
+ const key = `forecast_${f.id}`;
223
+ const color = f.color?.toString() ?? getForecastColor(index);
224
+ const api = chart.addSeries(LineSeries, {
225
+ color,
226
+ lineWidth: 1,
227
+ lineType: LineType.Curved,
228
+ lineStyle: forecastLineStyle === 'dashed' ? LineStyle.Dashed : LineStyle.Solid,
229
+ lastValueVisible: false,
230
+ priceLineVisible: false,
231
+ visible: !hiddenRef.current.has(key),
232
+ });
233
+ forecasts.set(key, api);
234
+ });
235
+ const historical = chart.addSeries(LineSeries, {
236
+ color: historicalLineColor,
237
+ lineWidth: 1,
238
+ lineType: LineType.Curved,
239
+ lineStyle: LineStyle.Solid,
240
+ lastValueVisible: false,
241
+ priceLineVisible: false,
242
+ visible: !hiddenRef.current.has('historical'),
243
+ });
244
+ modelRef.current = {
245
+ chart,
246
+ historical,
247
+ forecasts,
248
+ bands,
249
+ };
250
+ const onMove = (param) => {
251
+ if (!showTooltip) {
252
+ setTooltipState(s => ({ ...s, active: false, payload: [] }));
253
+ return;
254
+ }
255
+ const rows = bridgedRef.current;
256
+ if (!param.point || param.time === undefined || rows.length === 0) {
257
+ setTooltipState(s => ({ ...s, active: false, payload: [] }));
258
+ return;
259
+ }
260
+ const time = param.time;
261
+ const row = findNearestChartRow(rows, time);
262
+ if (!row) {
263
+ setTooltipState(s => ({ ...s, active: false, payload: [] }));
264
+ return;
265
+ }
266
+ const forecastsList = forecastRef.current;
267
+ const bandsCfg = quantileBandsRef.current;
268
+ const hid = hiddenRef.current;
269
+ const fmt = formatDateRef.current;
270
+ const histColor = histColorRef.current;
271
+ const payload = [];
272
+ const hVal = row.historical;
273
+ if (typeof hVal === 'number' && Number.isFinite(hVal)) {
274
+ payload.push({
275
+ type: 'line',
276
+ name: 'Historical Data',
277
+ value: hVal,
278
+ color: histColor,
279
+ dataKey: 'historical',
280
+ payload: row,
281
+ });
282
+ }
283
+ forecastsList.forEach((f, index) => {
284
+ const forecastKey = `forecast_${f.id}`;
285
+ if (hid.has(forecastKey))
286
+ return;
287
+ const value = row[forecastKey];
288
+ if (typeof value !== 'number' || !Number.isFinite(value))
289
+ return;
290
+ const color = f.color?.toString() ?? getForecastColor(index);
291
+ payload.push({
292
+ type: 'line',
293
+ name: f.name?.toString() ?? forecastKey,
294
+ value,
295
+ color,
296
+ dataKey: forecastKey,
297
+ payload: row,
298
+ });
299
+ });
300
+ bandsCfg?.forEach((band, index) => {
301
+ if (hid.has(band.key))
302
+ return;
303
+ const tuple = row[band.key];
304
+ if (Array.isArray(tuple) &&
305
+ tuple.length === 2 &&
306
+ typeof tuple[0] === 'number' &&
307
+ typeof tuple[1] === 'number') {
308
+ const color = band.color ?? getForecastQuantileBandColor(index);
309
+ payload.push({
310
+ type: 'line',
311
+ name: band.name,
312
+ value: [tuple[0], tuple[1]],
313
+ color,
314
+ dataKey: band.key,
315
+ payload: row,
316
+ });
317
+ }
318
+ });
319
+ if (!payload.length) {
320
+ setTooltipState(s => ({ ...s, active: false, payload: [] }));
321
+ return;
322
+ }
323
+ const label = fmt(row.date, true);
324
+ setTooltipState({
325
+ active: true,
326
+ x: param.point.x,
327
+ y: param.point.y,
328
+ label,
329
+ payload,
330
+ });
331
+ };
332
+ chart.subscribeCrosshairMove(onMove);
333
+ const resizeToHost = () => {
334
+ const ww = Math.max(1, Math.floor(host.clientWidth));
335
+ const hh = Math.max(1, Math.floor(host.clientHeight));
336
+ chart.resize(ww, hh);
337
+ scheduleFitTimeScale(chart);
338
+ };
339
+ let resizeObserver;
340
+ if (typeof ResizeObserver !== 'undefined') {
341
+ resizeObserver = new ResizeObserver(() => {
342
+ requestAnimationFrame(resizeToHost);
343
+ });
344
+ resizeObserver.observe(host);
345
+ }
346
+ requestAnimationFrame(resizeToHost);
347
+ return () => {
348
+ resizeObserver?.disconnect();
349
+ chart.unsubscribeCrosshairMove(onMove);
350
+ chart.remove();
351
+ modelRef.current = null;
352
+ };
353
+ }, [
354
+ structureKey,
355
+ isDarkTheme,
356
+ pixelHeight,
357
+ showTooltip,
358
+ bridgedChartData.length,
359
+ ]);
360
+ // Push data / band styles
361
+ useEffect(() => {
362
+ const model = modelRef.current;
363
+ if (!model)
364
+ return;
365
+ model.historical?.setData(buildHistoricalLineData(bridgedChartData));
366
+ for (const [key, api] of model.forecasts.entries()) {
367
+ api.setData(buildForecastLineData(bridgedChartData, key));
368
+ }
369
+ quantileBands?.forEach((band, index) => {
370
+ const entry = model.bands.get(band.key);
371
+ if (!entry)
372
+ return;
373
+ const fill = band.color ?? getForecastQuantileBandColor(index);
374
+ entry.view.updateStyle({
375
+ fill,
376
+ stroke: band.strokeWidth ? fill : undefined,
377
+ strokeWidth: band.strokeWidth ?? 0,
378
+ strokeDasharray: band.strokeDasharray,
379
+ strokeOpacity: band.strokeOpacity,
380
+ });
381
+ entry.api.applyOptions({ color: fill });
382
+ entry.api.setData(buildQuantileBandCustomData(bridgedChartData, band.key));
383
+ });
384
+ scheduleFitTimeScale(model.chart);
385
+ }, [bridgedChartData, quantileBands]);
386
+ // Visibility toggles
387
+ useEffect(() => {
388
+ const model = modelRef.current;
389
+ if (!model)
390
+ return;
391
+ model.historical?.applyOptions({
392
+ visible: !hiddenSeries.has('historical'),
393
+ });
394
+ for (const [key, api] of model.forecasts.entries()) {
395
+ api.applyOptions({ visible: !hiddenSeries.has(key) });
396
+ }
397
+ for (const [key, { api }] of model.bands.entries()) {
398
+ api.applyOptions({ visible: !hiddenSeries.has(key) });
399
+ }
400
+ scheduleFitTimeScale(model.chart);
401
+ }, [hiddenSeries]);
402
+ // Line styling updates without structural rebuild
403
+ useEffect(() => {
404
+ const model = modelRef.current;
405
+ if (!model?.historical)
406
+ return;
407
+ model.historical.applyOptions({ color: historicalLineColor });
408
+ }, [historicalLineColor]);
409
+ useEffect(() => {
410
+ const model = modelRef.current;
411
+ if (!model)
412
+ return;
413
+ const style = forecastLineStyle === 'dashed' ? LineStyle.Dashed : LineStyle.Solid;
414
+ for (const api of model.forecasts.values()) {
415
+ api.applyOptions({ lineStyle: style });
416
+ }
417
+ }, [forecastLineStyle]);
418
+ const handleLegendClick = useCallback((data, index, event) => {
419
+ const payloadItem = data;
420
+ const key = payloadItem.dataKey;
421
+ if (key) {
422
+ if (!hiddenControlled) {
423
+ setLocalHidden(prev => {
424
+ const next = new Set(prev);
425
+ if (next.has(key))
426
+ next.delete(key);
427
+ else
428
+ next.add(key);
429
+ return next;
430
+ });
431
+ }
432
+ }
433
+ onLegendClick?.(data, index, event);
434
+ }, [hiddenControlled, onLegendClick]);
435
+ if (error) {
436
+ return (jsx("div", { className: cn(S.root, className), children: jsxs("div", { style: { color: 'var(--destructive, #f43f5e)' }, children: ["Error: ", error] }) }));
437
+ }
438
+ if (loading) {
439
+ return (jsx("div", { className: cn(S.root, className), children: jsx("div", { style: { height: pixelHeight }, children: jsx(Skeleton, { style: { width: '100%', height: '100%' } }) }) }));
440
+ }
441
+ if (!bridgedChartData.length) {
442
+ return (jsx("div", { className: cn(S.root, className), children: jsx("div", { style: { height: pixelHeight }, children: noDataMessage }) }));
443
+ }
444
+ return (jsx(ChartContext.Provider, { value: { config: mergedChartConfig }, children: jsxs("div", { className: cn(S.root, className), children: [jsxs("div", { "data-slot": "chart", "data-chart": `chart-${chartId}`, className: S.shell, ref: shellRef, style: { position: 'relative', width: '100%' }, children: [jsx(ChartStyle, { id: `chart-${chartId}`, config: mergedChartConfig }), jsx("div", { ref: hostRef, className: S.host, style: { width: '100%', height: pixelHeight } }), showTooltip ? (jsx("div", { ref: tooltipRef, className: S.tooltipMove, style: {
445
+ opacity: tooltipState.active && tooltipState.payload.length ? 1 : 0,
446
+ }, children: jsx(ChartTooltipContent, { active: tooltipState.active && tooltipState.payload.length > 0, label: tooltipState.label, payload: tooltipState.active && tooltipState.payload.length
447
+ ? tooltipState.payload
448
+ : [], labelFormatter: lbl => formatDateFn(typeof lbl === 'string' ? lbl : String(lbl), true), formatter: formatNumber
449
+ ? (value, name) => {
450
+ const v = typeof value === 'number'
451
+ ? formatNumber(value)
452
+ : Array.isArray(value)
453
+ ? `${formatNumber(value[0])} – ${formatNumber(value[1])}`
454
+ : String(value ?? '');
455
+ return (jsxs(Fragment, { children: [jsxs("span", { children: [name, ": "] }), jsx("span", { children: v })] }));
456
+ }
457
+ : undefined }) })) : null] }), showLegend ? (jsx("div", { className: S.footer, children: jsx(CustomChartLegend, { payload: legendPayload, hiddenSeries: hiddenSeries, onClick: handleLegendClick }) })) : null] }) }));
458
+ }
459
+
460
+ export { LightweightForecastChart };
@@ -0,0 +1,7 @@
1
+ import styleInject from 'style-inject';
2
+
3
+ var css_248z = ".LightweightForecastChart_root__QK05J{display:flex;flex-direction:column;gap:12px;width:100%}.LightweightForecastChart_shell__fRJV5{position:relative;width:100%}.LightweightForecastChart_host__lxD2b{box-sizing:border-box;display:block;width:100%}.LightweightForecastChart_host__lxD2b #tv-attr-logo{display:none}.LightweightForecastChart_tooltipMove__BIKnO{pointer-events:none;position:absolute;z-index:5}.LightweightForecastChart_footer__t5N7A{width:100%}";
4
+ var S = {"root":"LightweightForecastChart_root__QK05J","shell":"LightweightForecastChart_shell__fRJV5","host":"LightweightForecastChart_host__lxD2b","tooltipMove":"LightweightForecastChart_tooltipMove__BIKnO","footer":"LightweightForecastChart_footer__t5N7A"};
5
+ styleInject(css_248z);
6
+
7
+ export { S as default };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Parse `YYYY-MM-DD` dates as UTC midnight → Lightweight Charts unix seconds.
3
+ */
4
+ function chartDateToUtcTimestamp(dateStr) {
5
+ const trimmed = dateStr.slice(0, 10);
6
+ const [y, m, d] = trimmed.split('-').map(Number);
7
+ if (!Number.isFinite(y) ||
8
+ !Number.isFinite(m) ||
9
+ !Number.isFinite(d) ||
10
+ trimmed.length !== 10) {
11
+ return Math.floor(new Date(dateStr).getTime() / 1000);
12
+ }
13
+ return Math.floor(Date.UTC(y, m - 1, d, 0, 0, 0, 0) / 1000);
14
+ }
15
+
16
+ export { chartDateToUtcTimestamp };