@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.
- package/assets/mini-app-global.css +12 -12
- package/dist/esm/components/ui/Chart/Chart.styl.js +1 -1
- package/dist/esm/components/ui/Chart/components/BaseChartWrapper.js +117 -170
- package/dist/esm/components/ui/ChartAreaInteractive/ChartAreaInteractive.helpers.js +101 -1
- package/dist/esm/components/ui/ChartAreaInteractive/ChartAreaInteractive.js +12 -4
- package/dist/esm/index.js +1 -0
- package/dist/esm/mini-app/MiniAppRoot.js +9 -5
- package/dist/esm/mini-app/miniAppThemeConfig.js +40 -0
- package/dist/esm/types/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.helpers.d.ts +6 -1
- package/dist/esm/types/src/docs/contexts/theme-context.d.ts +1 -0
- package/dist/esm/types/src/mini-app/MiniAppRoot.d.ts +4 -1
- package/dist/esm/types/src/mini-app/index.d.ts +4 -2
- package/dist/esm/types/src/mini-app/miniAppThemeConfig.d.ts +3 -0
- package/docs/workspace-mini-apps.md +3 -1
- package/package.json +1 -1
- package/src/components/ui/Chart/Chart.styl +7 -4
- package/src/components/ui/Chart/components/BaseChartWrapper.tsx +156 -193
- package/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.helpers.ts +90 -40
- package/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.tsx +15 -3
- package/src/docs/contexts/theme-context.tsx +9 -1
- package/src/docs/pages/ChartAreaInteractivePage.tsx +27 -1
- package/src/docs/pages/MiniAppRootPage.tsx +6 -1
- package/src/mini-app/MiniAppRoot.tsx +19 -1
- package/src/mini-app/index.ts +4 -8
- 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:
|
|
143
|
-
--page-color-alpha-800:
|
|
144
|
-
--background:
|
|
145
|
-
--background-alpha-800: oklch(
|
|
146
|
-
--background-alpha-700: oklch(
|
|
147
|
-
--background-alpha-500: oklch(
|
|
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.
|
|
200
|
-
--page-color-alpha-800: oklch(
|
|
201
|
-
--background: oklch(0.
|
|
202
|
-
--background-alpha-800: oklch(
|
|
203
|
-
--background-alpha-700: oklch(
|
|
204
|
-
--background-alpha-500: oklch(
|
|
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{
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
|
58
|
-
const
|
|
59
|
-
if (!wrapper || !
|
|
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
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
//
|
|
123
|
+
// Own only the final transform: smooth follow cursor, but clamp in local chart coords first.
|
|
98
124
|
useEffect(() => {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
165
|
-
|
|
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
|
-
|
|
177
|
-
if (rawTooltipCoordinateRef.current) {
|
|
178
|
-
rafId = requestAnimationFrame(monitorPosition);
|
|
179
|
-
}
|
|
179
|
+
window.addEventListener('resize', onWinResize);
|
|
180
180
|
return () => {
|
|
181
181
|
observer.disconnect();
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
200
|
+
rawTooltipCoordinateRef.current = {
|
|
223
201
|
x: props.coordinate.x,
|
|
224
202
|
y: props.coordinate.y,
|
|
225
203
|
};
|
|
226
|
-
|
|
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
|
|
236
|
-
|
|
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
|
-
?
|
|
42
|
-
: ensureChartForecastBridge(
|
|
49
|
+
? timeFilteredChartData
|
|
50
|
+
: ensureChartForecastBridge(timeFilteredChartData, {
|
|
43
51
|
forecastSeriesIds: forecastData?.map(f => f.id),
|
|
44
|
-
}), [
|
|
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
|
|
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 (
|
|
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 };
|