@sybilion/uilib 1.0.26 → 1.0.28

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 (25) hide show
  1. package/assets/mini-app-global.css +12 -12
  2. package/dist/esm/components/ui/Chart/Chart.styl.js +1 -1
  3. package/dist/esm/components/ui/Chart/components/BaseChartWrapper.js +117 -170
  4. package/dist/esm/components/ui/ChartAreaInteractive/ChartAreaInteractive.helpers.js +101 -1
  5. package/dist/esm/components/ui/ChartAreaInteractive/ChartAreaInteractive.js +12 -4
  6. package/dist/esm/index.js +1 -0
  7. package/dist/esm/mini-app/MiniAppRoot.js +9 -5
  8. package/dist/esm/mini-app/miniAppThemeConfig.js +40 -0
  9. package/dist/esm/types/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.helpers.d.ts +6 -1
  10. package/dist/esm/types/src/docs/contexts/theme-context.d.ts +1 -0
  11. package/dist/esm/types/src/mini-app/MiniAppRoot.d.ts +4 -1
  12. package/dist/esm/types/src/mini-app/index.d.ts +4 -2
  13. package/dist/esm/types/src/mini-app/miniAppThemeConfig.d.ts +3 -0
  14. package/docs/workspace-mini-apps.md +3 -1
  15. package/package.json +1 -1
  16. package/src/components/ui/Chart/Chart.styl +7 -4
  17. package/src/components/ui/Chart/components/BaseChartWrapper.tsx +156 -193
  18. package/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.helpers.ts +90 -40
  19. package/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.tsx +15 -3
  20. package/src/docs/contexts/theme-context.tsx +9 -1
  21. package/src/docs/pages/ChartAreaInteractivePage.tsx +27 -1
  22. package/src/docs/pages/MiniAppRootPage.tsx +6 -1
  23. package/src/mini-app/MiniAppRoot.tsx +19 -1
  24. package/src/mini-app/index.ts +4 -8
  25. package/src/mini-app/miniAppThemeConfig.ts +45 -0
@@ -139,12 +139,12 @@
139
139
  --header-height: 94px;
140
140
  --page-x-padding: var(--p-16);
141
141
  --page-y-padding: var(--p-12);
142
- --page-color: oklch(1 0 0);
143
- --page-color-alpha-800: oklch(from var(--page-color) l c h / 0.8);
144
- --background: var(--sb-slate-100);
145
- --background-alpha-800: oklch(from var(--background) l c h / 0.8);
146
- --background-alpha-700: oklch(from var(--background) l c h / 0.7);
147
- --background-alpha-500: oklch(from var(--background) l c h / 0.5);
142
+ --page-color: var(--sb-slate-100);
143
+ --page-color-alpha-800: var(--sb-slate-100);
144
+ --background: oklch(1 0 0);
145
+ --background-alpha-800: oklch(0.99 0 0 / 0.8);
146
+ --background-alpha-700: oklch(0.99 0 0 / 0.7);
147
+ --background-alpha-500: oklch(0.99 0 0 / 0.5);
148
148
  --foreground: oklch(0.15 0 0);
149
149
  --card: oklch(1 0 0);
150
150
  --card-foreground: oklch(0.15 0 0);
@@ -196,12 +196,12 @@
196
196
  }
197
197
 
198
198
  .dark {
199
- --page-color: oklch(0.14 0 0);
200
- --page-color-alpha-800: oklch(from var(--page-color) l c h / 0.95);
201
- --background: oklch(0.22 0 0);
202
- --background-alpha-800: oklch(from var(--background) l c h / 0.95);
203
- --background-alpha-700: oklch(from var(--background) l c h / 0.7);
204
- --background-alpha-500: oklch(from var(--background) l c h / 0.5);
199
+ --page-color: oklch(0.22 0 0);
200
+ --page-color-alpha-800: oklch(0.2242 0 0 / 0.8);
201
+ --background: oklch(0.14 0 0);
202
+ --background-alpha-800: oklch(0.14 0 0 / 0.95);
203
+ --background-alpha-700: oklch(0.14 0 0 / 0.7);
204
+ --background-alpha-500: oklch(0.14 0 0 / 0.5);
205
205
  --foreground: oklch(0.95 0 0);
206
206
  --card: oklch(0.1 0 0);
207
207
  --card-foreground: oklch(0.95 0 0);
@@ -1,6 +1,6 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = ".Chart_chartContainer__--q1l{aspect-ratio:16/9;display:flex;font-size:.75rem;justify-content:center;line-height:1rem;max-width:100%;touch-action:none;width:100%}.Chart_chartContainer__--q1l .recharts-cartesian-axis-tick text{fill:var(--muted-foreground)}.Chart_chartContainer__--q1l .recharts-cartesian-grid line[stroke=\"#ccc\"]{stroke:var(--border)}.dark .Chart_chartContainer__--q1l .recharts-cartesian-grid line[stroke=\"#ccc\"]{stroke:var(--sb-slate-900)}.Chart_chartContainer__--q1l .recharts-curve.recharts-tooltip-cursor,.Chart_chartContainer__--q1l .recharts-polar-grid [stroke=\"#ccc\"]{stroke:var(--border)}.Chart_chartContainer__--q1l .recharts-radial-bar-background-sector,.Chart_chartContainer__--q1l .recharts-rectangle.recharts-tooltip-cursor{fill:var(--muted)}.Chart_chartContainer__--q1l .recharts-reference-line [stroke=\"#ccc\"]{stroke:var(--border)}.Chart_chartContainer__--q1l .recharts-dot[stroke=\"#fff\"]{stroke:transparent}.Chart_chartContainer__--q1l .recharts-layer,.Chart_chartContainer__--q1l .recharts-sector{outline:none}.Chart_chartContainer__--q1l .recharts-sector[stroke=\"#fff\"]{stroke:transparent}.Chart_chartContainer__--q1l .recharts-surface{outline:none}.Chart_chartContainer__--q1l .recharts-wrapper{position:relative}.Chart_chartContainer__--q1l .recharts-surface{position:relative;z-index:1}.Chart_chartContainer__--q1l .recharts-tooltip-wrapper{position:relative;z-index:3!important}.Chart_chartContainer__--q1l .recharts-active-dot{z-index:3}.Chart_chartGrid__t52WF{stroke-width:.6}.Chart_tooltipContainer__6tc0q{align-items:start;display:grid;min-width:8rem;width:minmax(300px,100%);grid-gap:.375rem;background-color:var(--background);border:1px solid var(--border)/.5;border-radius:.5rem;box-shadow:0 10px 10px -5px rgba(0,0,0,.3),0 0 2px 0 rgba(0,0,0,.5);font-size:.75rem;gap:.375rem;line-height:1rem;opacity:0;padding:.375rem .625rem;transition:opacity .5s ease-out}.Chart_chartContainer__--q1l:hover .Chart_tooltipContainer__6tc0q{opacity:1;transition-duration:.5s}.Chart_tooltipItem__j8I9T{align-items:stretch;display:flex;flex-wrap:wrap;gap:.5rem;width:100%}.Chart_tooltipItem__j8I9T>svg{color:var(--muted-foreground);height:.625rem;width:.625rem}.Chart_tooltipIndicator__Z-JWp{background-color:var(--color-bg);border-color:var(--color-border);border-radius:2px;border-width:1px;flex-shrink:0}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-dot__MWcmW{height:.625rem;width:.625rem}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-line__MO3ul{width:.25rem}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-dashed__2LqIN{background-color:transparent;border-style:dashed;border-width:1.5px;width:0}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-dashed__2LqIN.Chart_nested__7EWWk{margin-bottom:.125rem;margin-top:.125rem}.Chart_tooltipContent__M3R-W{display:flex;flex:1 1 0%;justify-content:space-between;line-height:1}.Chart_tooltipLabel__zMpjZ{display:grid;grid-gap:.375rem;gap:.375rem}.Chart_tooltipValue__vTQxU{color:var(--foreground);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums;font-weight:500;margin-left:var(--p-3)}.Chart_legendContainer__u1J3U{align-items:center;display:flex;gap:1rem;justify-content:center}.Chart_legendItem__0CSyC{align-items:center;display:flex;gap:.375rem}.Chart_legendItem__0CSyC>svg{color:var(--muted-foreground);height:.75rem;width:.75rem}.Chart_legendIndicator__erzzP{border-radius:2px;flex-shrink:0;height:.5rem;width:.5rem}.chart-line-blinking path{animation:chart-line-blink 1s ease-in-out infinite;animation-direction:alternate}@keyframes Chart_chart-line-blink__4EI-g{0%{opacity:.5}to{opacity:1}}";
3
+ var css_248z = ".Chart_chartContainer__--q1l{aspect-ratio:16/9;display:flex;font-size:.75rem;justify-content:center;line-height:1rem;max-width:100%;touch-action:none;width:100%}.Chart_chartContainer__--q1l .recharts-cartesian-axis-tick text{fill:var(--muted-foreground)}.Chart_chartContainer__--q1l .recharts-cartesian-grid line[stroke=\"#ccc\"]{stroke:var(--border)}.dark .Chart_chartContainer__--q1l .recharts-cartesian-grid line[stroke=\"#ccc\"]{stroke:var(--sb-slate-900)}.Chart_chartContainer__--q1l .recharts-curve.recharts-tooltip-cursor,.Chart_chartContainer__--q1l .recharts-polar-grid [stroke=\"#ccc\"]{stroke:var(--border)}.Chart_chartContainer__--q1l .recharts-radial-bar-background-sector,.Chart_chartContainer__--q1l .recharts-rectangle.recharts-tooltip-cursor{fill:var(--muted)}.Chart_chartContainer__--q1l .recharts-reference-line [stroke=\"#ccc\"]{stroke:var(--border)}.Chart_chartContainer__--q1l .recharts-dot[stroke=\"#fff\"]{stroke:transparent}.Chart_chartContainer__--q1l .recharts-layer,.Chart_chartContainer__--q1l .recharts-sector{outline:none}.Chart_chartContainer__--q1l .recharts-sector[stroke=\"#fff\"]{stroke:transparent}.Chart_chartContainer__--q1l .recharts-surface{outline:none}.Chart_chartContainer__--q1l .recharts-wrapper{position:relative}.Chart_chartContainer__--q1l .recharts-surface{position:relative;z-index:1}.Chart_chartContainer__--q1l .recharts-tooltip-wrapper{z-index:3!important}.Chart_chartContainer__--q1l .recharts-active-dot{z-index:3}.Chart_chartGrid__t52WF{stroke-width:.6}.Chart_tooltipContainer__6tc0q{align-items:start;box-sizing:border-box;display:grid;max-width:100%;min-width:0;overflow-wrap:break-word;width:-moz-max-content;width:max-content;word-break:break-word;grid-gap:.375rem;background-color:var(--background);border:1px solid var(--border)/.5;border-radius:.5rem;box-shadow:0 10px 10px -5px rgba(0,0,0,.3),0 0 2px 0 rgba(0,0,0,.5);font-size:.75rem;gap:.375rem;line-height:1rem;opacity:0;padding:.375rem .625rem;transition:opacity .5s ease-out}.Chart_chartContainer__--q1l:hover .Chart_tooltipContainer__6tc0q{opacity:1;transition-duration:.5s}.Chart_tooltipItem__j8I9T{align-items:stretch;display:flex;flex-wrap:wrap;gap:.5rem;width:100%}.Chart_tooltipItem__j8I9T>svg{color:var(--muted-foreground);height:.625rem;width:.625rem}.Chart_tooltipIndicator__Z-JWp{background-color:var(--color-bg);border-color:var(--color-border);border-radius:2px;border-width:1px;flex-shrink:0}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-dot__MWcmW{height:.625rem;width:.625rem}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-line__MO3ul{width:.25rem}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-dashed__2LqIN{background-color:transparent;border-style:dashed;border-width:1.5px;width:0}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-dashed__2LqIN.Chart_nested__7EWWk{margin-bottom:.125rem;margin-top:.125rem}.Chart_tooltipContent__M3R-W{display:flex;flex:1 1 0%;justify-content:space-between;line-height:1}.Chart_tooltipLabel__zMpjZ{display:grid;grid-gap:.375rem;gap:.375rem}.Chart_tooltipValue__vTQxU{color:var(--foreground);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums;font-weight:500;margin-left:var(--p-3)}.Chart_legendContainer__u1J3U{align-items:center;display:flex;gap:1rem;justify-content:center}.Chart_legendItem__0CSyC{align-items:center;display:flex;gap:.375rem}.Chart_legendItem__0CSyC>svg{color:var(--muted-foreground);height:.75rem;width:.75rem}.Chart_legendIndicator__erzzP{border-radius:2px;flex-shrink:0;height:.5rem;width:.5rem}.chart-line-blinking path{animation:chart-line-blink 1s ease-in-out infinite;animation-direction:alternate}@keyframes Chart_chart-line-blink__4EI-g{0%{opacity:.5}to{opacity:1}}";
4
4
  var S = {"chartContainer":"Chart_chartContainer__--q1l","chartGrid":"Chart_chartGrid__t52WF","tooltipContainer":"Chart_tooltipContainer__6tc0q","tooltipItem":"Chart_tooltipItem__j8I9T","tooltipIndicator":"Chart_tooltipIndicator__Z-JWp","indicator-dot":"Chart_indicator-dot__MWcmW","indicator-line":"Chart_indicator-line__MO3ul","indicator-dashed":"Chart_indicator-dashed__2LqIN","nested":"Chart_nested__7EWWk","tooltipContent":"Chart_tooltipContent__M3R-W","tooltipLabel":"Chart_tooltipLabel__zMpjZ","tooltipValue":"Chart_tooltipValue__vTQxU","legendContainer":"Chart_legendContainer__u1J3U","legendItem":"Chart_legendItem__0CSyC","legendIndicator":"Chart_legendIndicator__erzzP","chart-line-blink":"Chart_chart-line-blink__4EI-g"};
5
5
  styleInject(css_248z);
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import cn from 'classnames';
3
- import { forwardRef, useRef, useState, useEffect, useMemo } from 'react';
3
+ import { forwardRef, useState, useRef, useMemo, useEffect } from 'react';
4
4
  import { ChartTooltip } from '../Chart.js';
5
5
  import { QuantileBands } from './QuantileBands.js';
6
6
  import { getForecastColor, ChartLines } from '../../ChartAreaInteractive/ChartLines.js';
@@ -16,6 +16,50 @@ import { ChartContainer } from './ChartContainer.js';
16
16
  import { CustomChartLegend } from './CustomChartLegend/CustomChartLegend.js';
17
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
+ function clampTooltipTranslate(args) {
45
+ const { coordinate, viewBox, tooltipWidth: tw, tooltipHeight: th, offset, edgeMargin, } = args;
46
+ const minX = viewBox.x + edgeMargin;
47
+ const maxX = viewBox.x + viewBox.width - tw - edgeMargin;
48
+ const minY = viewBox.y + edgeMargin;
49
+ const maxY = viewBox.y + viewBox.height - th - edgeMargin;
50
+ const clamp = (v, lo, hi) => Math.min(Math.max(v, lo), Math.max(lo, hi));
51
+ let tx = coordinate.x + offset;
52
+ if (tx + tw > viewBox.x + viewBox.width - edgeMargin) {
53
+ tx = coordinate.x - tw - offset;
54
+ }
55
+ tx = clamp(tx, minX, maxX);
56
+ let ty = coordinate.y + offset;
57
+ if (ty + th > viewBox.y + viewBox.height - edgeMargin) {
58
+ ty = coordinate.y - th - offset;
59
+ }
60
+ ty = clamp(ty, minY, maxY);
61
+ return { x: tx, y: ty };
62
+ }
19
63
  /**
20
64
  * Simple loading component without hooks for better performance
21
65
  * Rendered when chart is waiting in render queue
@@ -30,16 +74,8 @@ BaseChartWrapperLoading.displayName = 'BaseChartWrapperLoading';
30
74
  */
31
75
  const BaseChartWrapperContent = forwardRef((props, ref) => {
32
76
  const { chartConfig = {}, chartData, historicalLineColor, forecastData = [], loading, hasCombinedData, renderId, isDarkTheme, height, className, loadingComponentClassName, footerClassName, chartClassName, xAxisClassName, yAxisClassName, legendClassName, footerActions, quantileBands, quantileBandKey, xMin, xMax, yMin, yMax, autoScaleYAxis = true, formatNumber, formatDate: formatDateFn = formatDate, labelFormatter, onLegendClick, margin, chartType = 'composed', disableAnimation = false, showGrid = true, showAxes = true, showTooltip = true, showLegend = true, showChartAxesLegend = true, xAxisLabel, yAxisLabel, showActiveDots = true, overlayElements, hiddenSeries, excludeLegendIds, onAnalysisSelect, onFailedAnalysisClick, containerProps, error, loadingMessage, noDataMessage = 'No data available', onGridHeightChange, forecastLineStyle = 'dashed', disableHistoricalAnimation = false, onShowAll: _onShowAll, onShowOnly: _onShowOnly, maxVisibleItems, preventDeselection, legendVariant = 'default', legendWidth = 1000, legendMarginLeft = 0, } = props;
33
- const activeDataRef = useRef(null);
34
- /** Raw cursor position from Recharts (tooltip anchor). */
35
- const rawTooltipCoordinateRef = useRef(null);
36
- /** Last transform actually applied after viewport/boundary adjustment. */
37
- const lastAppliedTooltipPositionRef = useRef(null);
38
- const isTooltipActiveRef = useRef(false);
39
- // const [activeDotsData, setActiveDotsData] = useState<ActiveDot[]>([]);
40
77
  const [shouldAnimate, setShouldAnimate] = useState(false);
41
78
  const rootRef = useRef(null);
42
- // Merge forwarded ref with internal rootRef using callback ref
43
79
  const setRefs = (node) => {
44
80
  rootRef.current = node;
45
81
  if (typeof ref === 'function') {
@@ -49,107 +85,83 @@ const BaseChartWrapperContent = forwardRef((props, ref) => {
49
85
  ref.current = node;
50
86
  }
51
87
  };
52
- // const prevGridHeightRef = useRef<number>(0);
53
- const tooltipWrapperRef = useRef(null);
88
+ const resolvedChartMargin = useMemo(() => resolveChartMargin(margin), [margin?.top, margin?.right, margin?.bottom, margin?.left]);
54
89
  const TOOLTIP_EDGE_MARGIN = 8;
55
- const applyTooltipPosition = (opts) => {
90
+ const TOOLTIP_OFFSET = 10;
91
+ const tooltipWrapperRef = useRef(null);
92
+ const rawTooltipCoordinateRef = useRef(null);
93
+ const tooltipSizeRef = useRef({
94
+ width: 0,
95
+ height: 0,
96
+ });
97
+ const tooltipResizeObserverRef = useRef(null);
98
+ const tooltipResizeObservedTargetRef = useRef(null);
99
+ const applyTooltipPosition = () => {
56
100
  const wrapper = tooltipWrapperRef.current;
57
- const root = rootRef.current;
58
- const raw = rawTooltipCoordinateRef.current;
59
- if (!wrapper || !raw)
101
+ const coord = rawTooltipCoordinateRef.current;
102
+ const chartWrapper = rootRef.current?.querySelector('.recharts-wrapper');
103
+ if (!wrapper || !coord || !(chartWrapper instanceof HTMLElement))
60
104
  return;
61
- const setTransform = (el, x, y) => {
62
- el.style.transform = `translate(${x}px, ${y}px)`;
63
- lastAppliedTooltipPositionRef.current = { x, y };
64
- };
65
- setTransform(wrapper, raw.x, raw.y);
66
- if (opts?.skipBoundaryAdjust || !root)
105
+ const measured = wrapper.getBoundingClientRect();
106
+ const width = tooltipSizeRef.current.width || measured.width;
107
+ const height = tooltipSizeRef.current.height || measured.height;
108
+ if (width <= 0 || height <= 0)
67
109
  return;
68
- const runAdjust = () => {
69
- const w = tooltipWrapperRef.current;
70
- const rEl = rootRef.current;
71
- const rCoord = rawTooltipCoordinateRef.current;
72
- if (!w || !rEl || !rCoord)
73
- return;
74
- const rootRect = rEl.getBoundingClientRect();
75
- const rightBound = Math.min(rootRect.right, window.innerWidth);
76
- let x = rCoord.x;
77
- const y = rCoord.y;
78
- w.style.transform = `translate(${x}px, ${y}px)`;
79
- let tooltipRect = w.getBoundingClientRect();
80
- if (tooltipRect.right > rightBound - TOOLTIP_EDGE_MARGIN) {
81
- x -= tooltipRect.right - rightBound + TOOLTIP_EDGE_MARGIN;
82
- }
83
- const leftBound = Math.max(rootRect.left, 0);
84
- w.style.transform = `translate(${x}px, ${y}px)`;
85
- tooltipRect = w.getBoundingClientRect();
86
- if (tooltipRect.left < leftBound + TOOLTIP_EDGE_MARGIN) {
87
- x += leftBound + TOOLTIP_EDGE_MARGIN - tooltipRect.left;
88
- }
89
- setTransform(w, x, y);
90
- };
91
- requestAnimationFrame(() => {
92
- requestAnimationFrame(runAdjust);
110
+ tooltipSizeRef.current = { width, height };
111
+ const next = clampTooltipTranslate({
112
+ coordinate: coord,
113
+ viewBox: getPlotViewBox(chartWrapper, resolvedChartMargin),
114
+ tooltipWidth: width,
115
+ tooltipHeight: height,
116
+ offset: TOOLTIP_OFFSET,
117
+ edgeMargin: TOOLTIP_EDGE_MARGIN,
93
118
  });
119
+ wrapper.style.transform = `translate(${next.x}px, ${next.y}px)`;
94
120
  };
95
121
  const applyTooltipPositionRef = useRef(applyTooltipPosition);
96
122
  applyTooltipPositionRef.current = applyTooltipPosition;
97
- // Effect to watch for tooltip wrapper and apply transforms/opacity
123
+ // Own only the final transform: smooth follow cursor, but clamp in local chart coords first.
98
124
  useEffect(() => {
99
- const restorePosition = () => {
100
- const pos = lastAppliedTooltipPositionRef.current ??
101
- rawTooltipCoordinateRef.current;
102
- if (tooltipWrapperRef.current && pos) {
103
- const currentTransform = tooltipWrapperRef.current.style.transform;
104
- const expectedTransform = `translate(${pos.x}px, ${pos.y}px)`;
105
- // Always restore position if transform is missing, reset, or doesn't match expected position
106
- // This prevents Recharts from resetting the tooltip position
107
- if (!currentTransform ||
108
- currentTransform === 'none' ||
109
- currentTransform === 'translate(0px, 0px)' ||
110
- currentTransform !== expectedTransform) {
111
- tooltipWrapperRef.current.style.transform = expectedTransform;
112
- }
125
+ const connectResizeObserver = (wrapperEl) => {
126
+ if (tooltipResizeObservedTargetRef.current === wrapperEl) {
127
+ return;
113
128
  }
114
- };
115
- const findAndSetupTooltipWrapper = () => {
116
- const wrapper = rootRef.current?.querySelector('.recharts-tooltip-wrapper');
117
- if (wrapper && wrapper !== tooltipWrapperRef.current) {
118
- tooltipWrapperRef.current = wrapper;
119
- // Add transition for smooth movement and opacity
120
- wrapper.style.transition =
121
- 'transform 0.2s ease-out, opacity 0.2s ease-out';
122
- // Override Recharts' visibility: hidden with visibility: visible
123
- // We'll control visibility through opacity instead
124
- wrapper.style.visibility = 'visible';
125
- // Set initial opacity based on active state
126
- const isActive = activeDataRef.current?.active === true;
127
- wrapper.style.opacity = isActive ? '1' : '0';
128
- wrapper.style.pointerEvents = isActive ? 'auto' : 'none';
129
- // Always restore position if we have a last position
130
- restorePosition();
131
- if (rawTooltipCoordinateRef.current) {
132
- applyTooltipPositionRef.current();
133
- }
129
+ const resizeRo = tooltipResizeObserverRef.current ??
130
+ new ResizeObserver(entries => {
131
+ const entry = entries[0];
132
+ if (!entry)
133
+ return;
134
+ const { width, height } = entry.contentRect;
135
+ tooltipSizeRef.current = { width, height };
136
+ requestAnimationFrame(() => applyTooltipPositionRef.current());
137
+ });
138
+ tooltipResizeObserverRef.current = resizeRo;
139
+ const prevObserved = tooltipResizeObservedTargetRef.current;
140
+ if (prevObserved && prevObserved !== wrapperEl) {
141
+ resizeRo.unobserve(prevObserved);
134
142
  }
135
- // Always ensure visibility is visible (Recharts sets it to hidden)
136
- if (tooltipWrapperRef.current) {
137
- tooltipWrapperRef.current.style.visibility = 'visible';
138
- // Continuously restore position to prevent resets
139
- restorePosition();
143
+ tooltipResizeObservedTargetRef.current = wrapperEl;
144
+ resizeRo.observe(wrapperEl);
145
+ if (wrapperEl instanceof HTMLElement) {
146
+ tooltipWrapperRef.current = wrapperEl;
147
+ wrapperEl.style.transition =
148
+ 'transform 0.2s ease-out, opacity 0.2s ease-out';
149
+ requestAnimationFrame(() => applyTooltipPositionRef.current());
140
150
  }
141
151
  };
142
- // Initial check
143
- findAndSetupTooltipWrapper();
144
- // Watch for tooltip wrapper changes and style mutations
152
+ const tryObserveTooltipWrapper = () => {
153
+ const wrapper = rootRef.current?.querySelector('.recharts-tooltip-wrapper');
154
+ if (wrapper)
155
+ connectResizeObserver(wrapper);
156
+ };
157
+ tryObserveTooltipWrapper();
145
158
  const observer = new MutationObserver(mutations => {
146
- findAndSetupTooltipWrapper();
147
- // If transform attribute changed, restore position if needed
159
+ tryObserveTooltipWrapper();
148
160
  mutations.forEach(mutation => {
149
161
  if (mutation.type === 'attributes' &&
150
162
  mutation.attributeName === 'style' &&
151
163
  mutation.target === tooltipWrapperRef.current) {
152
- restorePosition();
164
+ requestAnimationFrame(() => applyTooltipPositionRef.current());
153
165
  }
154
166
  });
155
167
  });
@@ -161,27 +173,17 @@ const BaseChartWrapperContent = forwardRef((props, ref) => {
161
173
  attributeFilter: ['style'],
162
174
  });
163
175
  }
164
- // Use requestAnimationFrame to continuously monitor and restore position
165
- // Only run when we have a last position to maintain
166
- let rafId = null;
167
- const monitorPosition = () => {
168
- if (rawTooltipCoordinateRef.current && tooltipWrapperRef.current) {
169
- restorePosition();
170
- rafId = requestAnimationFrame(monitorPosition);
171
- }
172
- else {
173
- rafId = null;
174
- }
176
+ const onWinResize = () => {
177
+ requestAnimationFrame(() => applyTooltipPositionRef.current());
175
178
  };
176
- // Start monitoring if we already have a position
177
- if (rawTooltipCoordinateRef.current) {
178
- rafId = requestAnimationFrame(monitorPosition);
179
- }
179
+ window.addEventListener('resize', onWinResize);
180
180
  return () => {
181
181
  observer.disconnect();
182
- if (rafId !== null) {
183
- cancelAnimationFrame(rafId);
184
- }
182
+ tooltipResizeObserverRef.current?.disconnect();
183
+ tooltipResizeObserverRef.current = null;
184
+ tooltipResizeObservedTargetRef.current = null;
185
+ tooltipWrapperRef.current = null;
186
+ window.removeEventListener('resize', onWinResize);
185
187
  };
186
188
  }, []);
187
189
  const renderTooltipContent = (props) => {
@@ -191,73 +193,18 @@ const BaseChartWrapperContent = forwardRef((props, ref) => {
191
193
  // If no valid payload items, render ChartTooltipContent with active=false and empty payload
192
194
  // This allows ChartTooltipContent to clear its lastTooltipData state
193
195
  if (!filteredPayload || filteredPayload.length === 0) {
194
- // Update refs to reflect inactive state
195
- if (isTooltipActiveRef.current) {
196
- isTooltipActiveRef.current = false;
197
- // Always maintain last position when tooltip becomes inactive
198
- const pos = lastAppliedTooltipPositionRef.current ??
199
- rawTooltipCoordinateRef.current;
200
- if (pos && tooltipWrapperRef.current) {
201
- const wrapper = tooltipWrapperRef.current;
202
- wrapper.style.transform = `translate(${pos.x}px, ${pos.y}px)`;
203
- wrapper.style.visibility = 'visible';
204
- wrapper.style.opacity = '0';
205
- wrapper.style.pointerEvents = 'none';
206
- }
207
- }
208
- // Render ChartTooltipContent with active=false and empty payload to trigger cleanup
209
- // This ensures lastTooltipData is cleared when there's no valid data
210
196
  return (jsx(ChartTooltipContent, { active: false, payload: [], label: props.label, labelFormatter: labelFormatter || defaultLabelFormatter, indicator: "dot" }));
211
197
  }
212
- // Store tooltip data in ref (not state) to avoid re-render
213
- // Use filtered payload instead of original props.payload
214
- activeDataRef.current = {
215
- ...props,
216
- payload: filteredPayload,
217
- };
218
- const wasActive = isTooltipActiveRef.current;
219
198
  const isActive = props.active === true;
220
- // When tooltip becomes active and has coordinate, update last position
221
199
  if (isActive && props.coordinate) {
222
- const newCoordinate = {
200
+ rawTooltipCoordinateRef.current = {
223
201
  x: props.coordinate.x,
224
202
  y: props.coordinate.y,
225
203
  };
226
- rawTooltipCoordinateRef.current = newCoordinate;
227
- isTooltipActiveRef.current = true;
228
- if (tooltipWrapperRef.current) {
229
- tooltipWrapperRef.current.style.visibility = 'visible';
230
- tooltipWrapperRef.current.style.opacity = '1';
231
- tooltipWrapperRef.current.style.pointerEvents = 'auto';
232
- applyTooltipPosition();
233
- }
204
+ requestAnimationFrame(() => applyTooltipPositionRef.current());
234
205
  }
235
- else if (!isActive && wasActive) {
236
- isTooltipActiveRef.current = false;
237
- // Always maintain last position when tooltip becomes inactive
238
- const pos = lastAppliedTooltipPositionRef.current ??
239
- rawTooltipCoordinateRef.current;
240
- if (pos && tooltipWrapperRef.current) {
241
- const wrapper = tooltipWrapperRef.current;
242
- wrapper.style.transform = `translate(${pos.x}px, ${pos.y}px)`;
243
- // Keep visibility visible but hide with opacity transition
244
- wrapper.style.visibility = 'visible';
245
- wrapper.style.opacity = '0';
246
- wrapper.style.pointerEvents = 'none';
247
- }
248
- }
249
- else if (!isActive && !wasActive) {
250
- // Ensure opacity is 0 when inactive, but keep visibility visible and maintain position
251
- if (tooltipWrapperRef.current) {
252
- tooltipWrapperRef.current.style.visibility = 'visible';
253
- tooltipWrapperRef.current.style.opacity = '0';
254
- tooltipWrapperRef.current.style.pointerEvents = 'none';
255
- const pos = lastAppliedTooltipPositionRef.current ??
256
- rawTooltipCoordinateRef.current;
257
- if (pos) {
258
- tooltipWrapperRef.current.style.transform = `translate(${pos.x}px, ${pos.y}px)`;
259
- }
260
- }
206
+ else {
207
+ rawTooltipCoordinateRef.current = null;
261
208
  }
262
209
  return (jsx(ChartTooltipContent, { active: props.active, payload: filteredPayload, label: props.label, labelFormatter: labelFormatter || defaultLabelFormatter, indicator: "dot" }));
263
210
  };
@@ -385,7 +332,7 @@ const BaseChartWrapperContent = forwardRef((props, ref) => {
385
332
  }
386
333
  const ChartComponent = chartType === 'line' ? LineChart : ComposedChart;
387
334
  const defaultLabelFormatter = (v) => formatDateFn(v, true);
388
- 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, content: renderTooltipContent }) }))] }) }), overlayElements, jsxs("div", { className: cn(S.footer, footerClassName), children: [showLegend &&
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 &&
389
336
  (legendVariant === 'svg' ? (jsx(LegendSvg, { payload: legendPayload.map(p => ({
390
337
  value: p.value,
391
338
  color: p.color,
@@ -1,4 +1,104 @@
1
1
  // Helper function to format large numbers with k/m abbreviations
2
+ const timeRangeToMonths = {
3
+ '6m': 6,
4
+ '1y': 12,
5
+ '3y': 36,
6
+ '5y': 60,
7
+ All: 12,
8
+ };
9
+ function isPlottableNumber(value) {
10
+ return typeof value === 'number' && Number.isFinite(value);
11
+ }
12
+ /** Any row the chart can draw a number for (excludes date-only or empty rows) */
13
+ function hasPlottableChartSeriesValue(item) {
14
+ for (const [key, v] of Object.entries(item)) {
15
+ if (key === 'date')
16
+ continue;
17
+ if (isPlottableNumber(v))
18
+ return true;
19
+ if (Array.isArray(v) && v.some(x => isPlottableNumber(x))) {
20
+ return true;
21
+ }
22
+ }
23
+ return false;
24
+ }
25
+ /**
26
+ * Row counts toward the "end" of the window when anchoring the time range to a
27
+ * selected forecast (shared historical + that analysis line / quantiles).
28
+ */
29
+ function rowContributesToAnchoredTimeRange(item, analysisId) {
30
+ if (isPlottableNumber(item.historical)) {
31
+ return true;
32
+ }
33
+ const forecastKey = `forecast_${analysisId}`;
34
+ if (isPlottableNumber(item[forecastKey])) {
35
+ return true;
36
+ }
37
+ for (const key of Object.keys(item)) {
38
+ if (key.startsWith('q') && key.endsWith(`_${analysisId}`)) {
39
+ if (isPlottableNumber(item[key])) {
40
+ return true;
41
+ }
42
+ }
43
+ }
44
+ return false;
45
+ }
46
+ function computeLatestPlottableDate(data, options) {
47
+ const anchorId = options?.endDateAnchorAnalysisId;
48
+ const pick = (item, useAnchor) => {
49
+ if (!item.date)
50
+ return null;
51
+ if (useAnchor && anchorId != null) {
52
+ if (!rowContributesToAnchoredTimeRange(item, anchorId)) {
53
+ return null;
54
+ }
55
+ }
56
+ else if (!hasPlottableChartSeriesValue(item)) {
57
+ return null;
58
+ }
59
+ return new Date(item.date);
60
+ };
61
+ let latest = null;
62
+ for (const item of data) {
63
+ const d = pick(item, true);
64
+ if (d && (!latest || d > latest)) {
65
+ latest = d;
66
+ }
67
+ }
68
+ if (latest == null && anchorId != null) {
69
+ for (const item of data) {
70
+ const d = pick(item, false);
71
+ if (d && (!latest || d > latest)) {
72
+ latest = d;
73
+ }
74
+ }
75
+ }
76
+ return latest;
77
+ }
78
+ const filterDataForTimeRange = (data, currentTimeRange, options) => {
79
+ if (currentTimeRange === 'All')
80
+ return data;
81
+ const latestDate = computeLatestPlottableDate(data, options);
82
+ // Pre-compute start date based on latest date in data
83
+ let startDate = null;
84
+ if (latestDate) {
85
+ const monthsToSubtract = timeRangeToMonths[currentTimeRange] || timeRangeToMonths.All;
86
+ startDate = new Date(latestDate);
87
+ startDate.setMonth(startDate.getMonth() - monthsToSubtract);
88
+ }
89
+ // Slice by date for every row. Rows with forecast_* keys must not bypass the
90
+ // window (e.g. spaghetti plots), or the X range stays stuck at full history.
91
+ const filteredData = data.filter(item => {
92
+ if (!item.date)
93
+ return false;
94
+ if (startDate) {
95
+ const date = new Date(item.date);
96
+ return date >= startDate;
97
+ }
98
+ return true;
99
+ });
100
+ return filteredData;
101
+ };
2
102
  const shortDateFormatter = (value) => {
3
103
  return new Date(value).toLocaleDateString('en-US', {
4
104
  month: 'short',
@@ -12,4 +112,4 @@ const longDateFormatter = (value) => {
12
112
  });
13
113
  };
14
114
 
15
- export { longDateFormatter, shortDateFormatter };
115
+ export { filterDataForTimeRange, longDateFormatter, shortDateFormatter };
@@ -5,7 +5,7 @@ import { BaseChartWrapper } from '../Chart/components/BaseChartWrapper.js';
5
5
  import { InteractionOverlay } from '../InteractionOverlay/InteractionOverlay.js';
6
6
  import { TimeRangeControls } from '../TimeRangeControls/TimeRangeControls.js';
7
7
  import { ensureChartForecastBridge } from '../../../utils/chartConnectionPoint.js';
8
- import { shortDateFormatter, longDateFormatter } from './ChartAreaInteractive.helpers.js';
8
+ import { filterDataForTimeRange, shortDateFormatter, longDateFormatter } from './ChartAreaInteractive.helpers.js';
9
9
  import S from './ChartAreaInteractive.styl.js';
10
10
  import { PinOverlay } from './overlays/PinOverlay/PinOverlay.js';
11
11
  import { IntervalsOverlay } from './overlays/IntervalsOverlay/IntervalsOverlay.js';
@@ -37,11 +37,19 @@ function ChartAreaInteractive({ className, chartContainerClassName, legendClassN
37
37
  ensureAnalysisSeriesVisible(selectedAnalysisId);
38
38
  }
39
39
  }, [selectedAnalysisId, ensureAnalysisSeriesVisible]);
40
+ const timeFilteredChartData = useMemo(() => {
41
+ const raw = selectedAnalysisId ?? selectedForecast?.id ?? null;
42
+ const anchorId = raw == null ? null : typeof raw === 'number' ? raw : Number(raw);
43
+ const opts = anchorId != null && Number.isFinite(anchorId)
44
+ ? { endDateAnchorAnalysisId: anchorId }
45
+ : undefined;
46
+ return filterDataForTimeRange(chartData, timeRange, opts);
47
+ }, [chartData, timeRange, selectedAnalysisId, selectedForecast?.id]);
40
48
  const bridgedChartData = useMemo(() => disableForecastHistoricalBridge
41
- ? chartData
42
- : ensureChartForecastBridge(chartData, {
49
+ ? timeFilteredChartData
50
+ : ensureChartForecastBridge(timeFilteredChartData, {
43
51
  forecastSeriesIds: forecastData?.map(f => f.id),
44
- }), [chartData, disableForecastHistoricalBridge, forecastData]);
52
+ }), [timeFilteredChartData, disableForecastHistoricalBridge, forecastData]);
45
53
  // Extract quantileBands from restProps
46
54
  // const quantileBands = (restProps as any).quantileBands;
47
55
  const getLoadingMessage = () => {
package/dist/esm/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { MINIAPP_CHANNEL, MINIAPP_VERSION, applyThemeToDocument, buildReadyMessage, parseThemeSyncMessage, resolveParentOriginFromReferrer } from './mini-app/miniAppProtocol.js';
2
+ export { getDefaultMiniAppThemeConfig } from './mini-app/miniAppThemeConfig.js';
2
3
  export { MiniAppRoot, useMiniAppShellTheme } from './mini-app/MiniAppRoot.js';
3
4
  export { ChatContext, ChatProvider, useChat, useChats, useChatsForDataset, useChatsForScopeId, useCurrentChat } from './contexts/chat-context.js';
4
5
  export { AnalysesSelector } from './components/ui/AnalysesSelector/AnalysesSelector.js';
@@ -1,9 +1,10 @@
1
- import { jsx } from 'react/jsx-runtime';
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import cn from 'classnames';
3
- import { createContext, useContext, useState, useRef, useCallback, useEffect, useMemo } from 'react';
4
- import { Scroll } from '@homecode/ui';
3
+ import { createContext, useContext, useState, useRef, useMemo, useCallback, useEffect } from 'react';
4
+ import { Theme, Scroll } from '@homecode/ui';
5
5
  import S from './MiniAppRoot.styl.js';
6
6
  import { resolveParentOriginFromReferrer, applyThemeToDocument, buildReadyMessage, parseThemeSyncMessage } from './miniAppProtocol.js';
7
+ import { getDefaultMiniAppThemeConfig } from './miniAppThemeConfig.js';
7
8
 
8
9
  const defaultTheme = {
9
10
  mode: 'light',
@@ -45,10 +46,13 @@ function isTrustedParentMessage(event) {
45
46
  return false;
46
47
  return true;
47
48
  }
48
- function MiniAppRoot({ children, className, appId, onThemeChange, }) {
49
+ function MiniAppRoot({ children, className, appId, onThemeChange, getThemeConfig, }) {
49
50
  const [theme, setTheme] = useState(() => isEmbeddedMiniApp() ? defaultTheme : themeFromDocument());
50
51
  const onThemeChangeRef = useRef(onThemeChange);
51
52
  onThemeChangeRef.current = onThemeChange;
53
+ const getThemeConfigRef = useRef(getThemeConfig ?? getDefaultMiniAppThemeConfig);
54
+ getThemeConfigRef.current = getThemeConfig ?? getDefaultMiniAppThemeConfig;
55
+ const currThemeConfig = useMemo(() => getThemeConfigRef.current(theme.isDarkMode), [theme.isDarkMode]);
52
56
  const sendReady = useCallback(() => {
53
57
  if (!window.parent || window.parent === window)
54
58
  return;
@@ -88,7 +92,7 @@ function MiniAppRoot({ children, className, appId, onThemeChange, }) {
88
92
  return () => window.removeEventListener('load', sendReady);
89
93
  }, [sendReady]);
90
94
  const ctx = useMemo(() => ({ theme }), [theme]);
91
- return (jsx(MiniAppShellContext.Provider, { value: ctx, children: jsx(Scroll, { y: true, fadeSize: "l", className: cn(S.root, className), innerClassName: S.inner, offset: { y: { before: 50, after: 50 } }, autoHide: true, children: children }) }));
95
+ return (jsxs(MiniAppShellContext.Provider, { value: ctx, children: [jsx(Theme, { config: currThemeConfig }), jsx(Scroll, { y: true, fadeSize: "l", className: cn(S.root, className), innerClassName: S.inner, offset: { y: { before: 50, after: 50 } }, autoHide: true, children: children })] }));
92
96
  }
93
97
 
94
98
  export { MiniAppRoot, useMiniAppShellTheme };