@sybilion/uilib 1.3.53 → 1.3.54
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/dist/esm/components/ui/Card/Card.js +6 -6
- package/dist/esm/components/ui/Dialog/Dialog.js +2 -2
- package/dist/esm/components/widgets/DriversComparisonChart/DriversComparisonChart.js +4 -4
- package/dist/esm/types/src/components/ui/Card/Card.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Card/Card.types.d.ts +1 -0
- package/dist/esm/types/src/components/ui/Dialog/Dialog.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Dialog/Dialog.types.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/ui/Card/Card.tsx +13 -4
- package/src/components/ui/Card/Card.types.ts +1 -0
- package/src/components/ui/Dialog/Dialog.tsx +2 -0
- package/src/components/ui/Dialog/Dialog.types.ts +1 -0
- package/src/components/widgets/DriversComparisonChart/DriversComparisonChart.tsx +43 -2
|
@@ -24,12 +24,12 @@ function CardDescription({ className, ...props }) {
|
|
|
24
24
|
function CardAction({ className, ...props }) {
|
|
25
25
|
return (jsx("div", { "data-slot": "card-action", className: cn(S.action, className), ...props }));
|
|
26
26
|
}
|
|
27
|
-
function CardContent({ className, centered, fullHeight, noScroll, children, autoScrollBottom, ...props }) {
|
|
28
|
-
const
|
|
27
|
+
function CardContent({ className, centered, fullHeight, noScroll, children, autoScrollBottom, yScrollbarClassName, ...props }) {
|
|
28
|
+
const scrollInnerRef = useRef(null);
|
|
29
29
|
const contentDiv = (jsx("div", { "data-slot": "card-content", className: cn(className, centered && S.centered, fullHeight && S.fullHeight), ...props, children: children }));
|
|
30
30
|
useEffect(() => {
|
|
31
31
|
if (!noScroll && autoScrollBottom) {
|
|
32
|
-
const inner =
|
|
32
|
+
const inner = scrollInnerRef.current;
|
|
33
33
|
if (!inner)
|
|
34
34
|
return;
|
|
35
35
|
if (inner.scrollHeight > SCROLL_OFFSET) {
|
|
@@ -46,9 +46,9 @@ function CardContent({ className, centered, fullHeight, noScroll, children, auto
|
|
|
46
46
|
}, [noScroll, autoScrollBottom]);
|
|
47
47
|
if (noScroll)
|
|
48
48
|
return contentDiv;
|
|
49
|
-
return (
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
return (jsx(Scroll, { y: true, autoHide: true, fadeSize: "m", className: S.scroll, yScrollbarClassName: yScrollbarClassName, onInnerRef: el => {
|
|
50
|
+
scrollInnerRef.current = el;
|
|
51
|
+
}, children: contentDiv }));
|
|
52
52
|
}
|
|
53
53
|
function CardFooter({ className, ...props }) {
|
|
54
54
|
return (jsx("div", { "data-slot": "card-footer", className: cn(S.footer, className), ...props }));
|
|
@@ -7,7 +7,7 @@ import { APP_MODAL_ID } from '../../../constants/appMount.js';
|
|
|
7
7
|
import { XIcon } from '@phosphor-icons/react';
|
|
8
8
|
import S from './Dialog.styl.js';
|
|
9
9
|
|
|
10
|
-
function Dialog({ open, onOpenChange, disabled, trigger, title, subtitle, disableCloseButton, icon, content, contentClassName, footer, footerClassName, footerAlignment = 'center', size = 'default', className, noScroll, maxHeight, autoScrollBottom, width, }) {
|
|
10
|
+
function Dialog({ open, onOpenChange, disabled, trigger, title, subtitle, disableCloseButton, icon, content, contentClassName, contentScrollbarClassName, footer, footerClassName, footerAlignment = 'center', size = 'default', className, noScroll, maxHeight, autoScrollBottom, width, }) {
|
|
11
11
|
const [isMounted, setIsMounted] = useState(false);
|
|
12
12
|
const [modalContainer, setModalContainer] = useState(null);
|
|
13
13
|
const [isAnimating, setIsAnimating] = useState(false);
|
|
@@ -123,7 +123,7 @@ function Dialog({ open, onOpenChange, disabled, trigger, title, subtitle, disabl
|
|
|
123
123
|
maxHeight: maxHeight
|
|
124
124
|
? `min(${maxHeight}, calc(100% - var(--p-4)))`
|
|
125
125
|
: null,
|
|
126
|
-
}, children: [(title || subtitle || icon) && (jsx(CardHeader, { icon: icon, title: title, description: subtitle })), !disableCloseButton && (jsx("button", { className: S.dialogClose, onClick: handleCloseClick, "aria-label": "Close dialog", type: "button", children: jsx(XIcon, { size: 16 }) })), content && (jsx(CardContent, { className: contentClassName, noScroll: noScroll, autoScrollBottom: autoScrollBottom, children: content })), footer && (jsx(CardFooter, { className: cn(S.footer, S[`align-${footerAlignment}`], footerClassName), children: footer }))] })] }));
|
|
126
|
+
}, children: [(title || subtitle || icon) && (jsx(CardHeader, { icon: icon, title: title, description: subtitle })), !disableCloseButton && (jsx("button", { className: S.dialogClose, onClick: handleCloseClick, "aria-label": "Close dialog", type: "button", children: jsx(XIcon, { size: 16 }) })), content && (jsx(CardContent, { className: contentClassName, yScrollbarClassName: contentScrollbarClassName, noScroll: noScroll, autoScrollBottom: autoScrollBottom, children: content })), footer && (jsx(CardFooter, { className: cn(S.footer, S[`align-${footerAlignment}`], footerClassName), children: footer }))] })] }));
|
|
127
127
|
const onTriggerClick = !disabled ? () => onOpenChange(true) : undefined;
|
|
128
128
|
return (jsxs(Fragment, { children: [trigger && jsx("div", { onClick: onTriggerClick, children: trigger }), open &&
|
|
129
129
|
!disabled &&
|
|
@@ -7,10 +7,10 @@ import { getForecastColor } from '../../ui/ChartAreaInteractive/ChartLines.js';
|
|
|
7
7
|
import '../../ui/Page/AppShell/AppShell.js';
|
|
8
8
|
import 'react-router-dom';
|
|
9
9
|
import '../../ui/Sidebar/Sidebar.js';
|
|
10
|
-
import 'lucide-react';
|
|
10
|
+
import { InfoIcon } from 'lucide-react';
|
|
11
11
|
import '../../ui/Page/Breadcrumbs/Breadcrumbs.styl.js';
|
|
12
12
|
import '../../ui/Page/pageContext.js';
|
|
13
|
-
import '../../ui/Tooltip/Tooltip.js';
|
|
13
|
+
import { Tooltip, TooltipTrigger, TooltipContent } from '../../ui/Tooltip/Tooltip.js';
|
|
14
14
|
import '../../ui/Page/PageHeader/PageHeader.styl.js';
|
|
15
15
|
import '../../ui/Page/PageEmptyCanvas/PageEmptyCanvas.styl.js';
|
|
16
16
|
import '../../ui/Page/PageContent/PageContent.styl.js';
|
|
@@ -24,7 +24,7 @@ import '../../ui/Tabs/Tabs.styl.js';
|
|
|
24
24
|
import '../../ui/Page/PageTabs/PageTabs.styl.js';
|
|
25
25
|
import '../../ui/Page/PageColumns/PageColumns.styl.js';
|
|
26
26
|
import '../../ui/Page/SectionHeader/SectionHeader.styl.js';
|
|
27
|
-
import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from '../../ui/Table/Table.js';
|
|
27
|
+
import { Table, TableHeader, TableRow, TableHead, TableCellValue, TableBody, TableCell } from '../../ui/Table/Table.js';
|
|
28
28
|
import { TextShimmer } from '../../ui/TextShimmer/TextShimmer.js';
|
|
29
29
|
import { TIME_RANGES } from '../../ui/TimeRangeControls/TimeRangeControls.constants.js';
|
|
30
30
|
import S from './DriversComparisonChart.styl.js';
|
|
@@ -127,7 +127,7 @@ function DriversComparisonChart({ payload, datasetHistorical = [], loading = fal
|
|
|
127
127
|
const showEmptyOverlay = (runAnalysisHint || Boolean(statusHint)) && !loading;
|
|
128
128
|
return (jsxs("div", { className: cn(S.root, className), children: [jsxs("div", { className: cn(S.chartShell, loading && S.chartShellLoading), children: [jsx("div", { className: S.chartSlot, children: jsxs("div", { className: S.chartWithOverlay, children: [jsx("div", { className: cn(S.chartInteractiveLayer, showEmptyOverlay && S.chartInteractiveDimmed), children: jsx(ChartAreaInteractive, { disableHistoricalAnimation: true, disableTimeRangeSelector: true, enableTimeRangeBrush: true, chartContainerClassName: S.chartContainer, chartData: driversComparisonChartData, forecastData: chartForecastData, timeRange: timeRange, onTimeRangeChange: handleTimeRangeChange, pinMonth: undefined, onPinMonthChange: () => { }, isDarkTheme: isDarkTheme, loading: chartLoading, hasCombinedData: mergedWithHistorical.length > 0, forecastLineStyle: "solid", showLegend: false, hiddenSeries: hiddenSeries, toggleLegendSeries: toggleSeries, ensureAnalysisSeriesVisible: showSeries }) }), showEmptyOverlay && (jsx("div", { className: S.chartEmptyOverlay, role: "status", "aria-live": "polite", children: jsx("div", { className: S.chartEmptyBlurb, children: jsx(ChartEmptyState, { variant: "inline", hint: runAnalysisHint
|
|
129
129
|
? 'Run a completed analysis to load the drivers comparison chart for an analysis.'
|
|
130
|
-
: undefined, status: statusHint ?? undefined, statusTone: statusTone }) }) }))] }) }), loading && (jsx("div", { className: S.loadingLayer, "aria-busy": "true", "aria-live": "polite", children: jsx("div", { className: S.loadingMessage, children: jsx(TextShimmer, { as: "span", className: S.loadingText, children: "Loading drivers comparison\u2026" }) }) }))] }), jsx("div", { className: S.seriesSection, children: tableSeriesRows.length === 0 ? (jsx("div", { className: S.seriesEmptyWrap, children: jsx("div", { className: S.chartEmptyBlurb, children: jsx(ChartEmptyState, { variant: "inline", align: "center", status: "No series" }) }) })) : (jsx("div", { className: S.seriesTableWrapper, children: jsx(PageXScroll, { size: "md", fullWidth: true, innerClassName: S.seriesTableContainer, scrollbarClassName: S.seriesScrollbar, children: jsxs(Table, { withBackground: true, withPaddings: true, className: S.seriesTable, children: [jsx(TableHeader, { children: jsxs(TableRow, { children: [jsx(TableHead, { className: S.seriesColSeries, children: "Driver name" }), jsx(TableHead, { children: "Importance" }), jsx(TableHead, { children: "Lag" })] }) }), jsx(TableBody, { children: tableSeriesRows.map(row => {
|
|
130
|
+
: undefined, status: statusHint ?? undefined, statusTone: statusTone }) }) }))] }) }), loading && (jsx("div", { className: S.loadingLayer, "aria-busy": "true", "aria-live": "polite", children: jsx("div", { className: S.loadingMessage, children: jsx(TextShimmer, { as: "span", className: S.loadingText, children: "Loading drivers comparison\u2026" }) }) }))] }), jsx("div", { className: S.seriesSection, children: tableSeriesRows.length === 0 ? (jsx("div", { className: S.seriesEmptyWrap, children: jsx("div", { className: S.chartEmptyBlurb, children: jsx(ChartEmptyState, { variant: "inline", align: "center", status: "No series" }) }) })) : (jsx("div", { className: S.seriesTableWrapper, children: jsx(PageXScroll, { size: "md", fullWidth: true, innerClassName: S.seriesTableContainer, scrollbarClassName: S.seriesScrollbar, children: jsxs(Table, { withBackground: true, withPaddings: true, className: S.seriesTable, children: [jsx(TableHeader, { children: jsxs(TableRow, { children: [jsx(TableHead, { className: S.seriesColSeries, children: "Driver name" }), jsx(TableHead, { children: jsxs(TableCellValue, { children: ["Importance", jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("span", { children: jsx(InfoIcon, { size: 16, style: { cursor: 'help' } }) }) }), jsx(TooltipContent, { side: "top", maxWidth: 300, children: "How much this driver contributes to price movements in the forecast model. Higher = stronger influence. Reflects relative weight of each driver, scored from 0% to 100%." })] })] }) }), jsx(TableHead, { children: jsxs(TableCellValue, { children: ["Lag", jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("span", { children: jsx(InfoIcon, { size: 16, style: { cursor: 'help' } }) }) }), jsx(TooltipContent, { side: "top", maxWidth: 300, children: "Lag shows how far ahead this driver predicts price movement. A range (e.g. 9\u201312 months) means the predictive signal is strongest somewhere in that window \u2014 not at a single fixed point." })] })] }) })] }) }), jsx(TableBody, { children: tableSeriesRows.map(row => {
|
|
131
131
|
const dataKey = toForecastDataKey(row.id);
|
|
132
132
|
const hidden = hiddenSeries.has(dataKey);
|
|
133
133
|
return (jsxs(TableRow, { className: cn(hidden && S.rowHidden), onClick: () => toggleSeries(row.id), children: [jsx(TableCell, { children: jsxs("span", { className: S.seriesLabel, children: [jsx("span", { className: S.colorSwatch, style: {
|
|
@@ -6,6 +6,6 @@ declare function CardHeader({ className, icon, iconWithBackground, textsClassNam
|
|
|
6
6
|
declare function CardTitle({ className, ...props }: CardTitleProps): import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
declare function CardDescription({ className, ...props }: CardDescriptionProps): import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
declare function CardAction({ className, ...props }: CardActionProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
-
declare function CardContent({ className, centered, fullHeight, noScroll, children, autoScrollBottom, ...props }: CardContentProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
declare function CardContent({ className, centered, fullHeight, noScroll, children, autoScrollBottom, yScrollbarClassName, ...props }: CardContentProps): import("react/jsx-runtime").JSX.Element;
|
|
10
10
|
declare function CardFooter({ className, ...props }: CardFooterProps): import("react/jsx-runtime").JSX.Element;
|
|
11
11
|
export { Card, LinkCard, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, };
|
|
@@ -24,6 +24,7 @@ export interface CardContentProps extends React.ComponentProps<'div'> {
|
|
|
24
24
|
fullHeight?: boolean;
|
|
25
25
|
noScroll?: boolean;
|
|
26
26
|
autoScrollBottom?: boolean;
|
|
27
|
+
yScrollbarClassName?: string;
|
|
27
28
|
}
|
|
28
29
|
export interface CardFooterProps extends React.ComponentProps<'div'> {
|
|
29
30
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { DialogProps } from './Dialog.types';
|
|
2
|
-
export declare function Dialog({ open, onOpenChange, disabled, trigger, title, subtitle, disableCloseButton, icon, content, contentClassName, footer, footerClassName, footerAlignment, size, className, noScroll, maxHeight, autoScrollBottom, width, }: DialogProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function Dialog({ open, onOpenChange, disabled, trigger, title, subtitle, disableCloseButton, icon, content, contentClassName, contentScrollbarClassName, footer, footerClassName, footerAlignment, size, className, noScroll, maxHeight, autoScrollBottom, width, }: DialogProps): import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
export { default as DialogStyles } from './Dialog.styl';
|
package/package.json
CHANGED
|
@@ -129,9 +129,10 @@ function CardContent({
|
|
|
129
129
|
noScroll,
|
|
130
130
|
children,
|
|
131
131
|
autoScrollBottom,
|
|
132
|
+
yScrollbarClassName,
|
|
132
133
|
...props
|
|
133
134
|
}: CardContentProps) {
|
|
134
|
-
const
|
|
135
|
+
const scrollInnerRef = useRef<HTMLDivElement | null>(null);
|
|
135
136
|
|
|
136
137
|
const contentDiv = (
|
|
137
138
|
<div
|
|
@@ -149,7 +150,7 @@ function CardContent({
|
|
|
149
150
|
|
|
150
151
|
useEffect(() => {
|
|
151
152
|
if (!noScroll && autoScrollBottom) {
|
|
152
|
-
const inner =
|
|
153
|
+
const inner = scrollInnerRef.current;
|
|
153
154
|
|
|
154
155
|
if (!inner) return;
|
|
155
156
|
|
|
@@ -170,8 +171,16 @@ function CardContent({
|
|
|
170
171
|
if (noScroll) return contentDiv;
|
|
171
172
|
|
|
172
173
|
return (
|
|
173
|
-
|
|
174
|
-
|
|
174
|
+
<Scroll
|
|
175
|
+
y
|
|
176
|
+
autoHide
|
|
177
|
+
fadeSize="m"
|
|
178
|
+
className={S.scroll}
|
|
179
|
+
yScrollbarClassName={yScrollbarClassName}
|
|
180
|
+
onInnerRef={el => {
|
|
181
|
+
scrollInnerRef.current = el;
|
|
182
|
+
}}
|
|
183
|
+
>
|
|
175
184
|
{contentDiv}
|
|
176
185
|
</Scroll>
|
|
177
186
|
);
|
|
@@ -30,6 +30,7 @@ export interface CardContentProps extends React.ComponentProps<'div'> {
|
|
|
30
30
|
fullHeight?: boolean;
|
|
31
31
|
noScroll?: boolean;
|
|
32
32
|
autoScrollBottom?: boolean;
|
|
33
|
+
yScrollbarClassName?: string;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
export interface CardFooterProps extends React.ComponentProps<'div'> {}
|
|
@@ -31,6 +31,7 @@ export function Dialog({
|
|
|
31
31
|
icon,
|
|
32
32
|
content,
|
|
33
33
|
contentClassName,
|
|
34
|
+
contentScrollbarClassName,
|
|
34
35
|
footer,
|
|
35
36
|
footerClassName,
|
|
36
37
|
footerAlignment = 'center',
|
|
@@ -215,6 +216,7 @@ export function Dialog({
|
|
|
215
216
|
{content && (
|
|
216
217
|
<CardContent
|
|
217
218
|
className={contentClassName}
|
|
219
|
+
yScrollbarClassName={contentScrollbarClassName}
|
|
218
220
|
noScroll={noScroll}
|
|
219
221
|
autoScrollBottom={autoScrollBottom}
|
|
220
222
|
>
|
|
@@ -13,13 +13,20 @@ import {
|
|
|
13
13
|
Table,
|
|
14
14
|
TableBody,
|
|
15
15
|
TableCell,
|
|
16
|
+
TableCellValue,
|
|
16
17
|
TableHead,
|
|
17
18
|
TableHeader,
|
|
18
19
|
TableRow,
|
|
19
20
|
} from '#uilib/components/ui/Table';
|
|
20
21
|
import { TextShimmer } from '#uilib/components/ui/TextShimmer/TextShimmer';
|
|
21
22
|
import { TIME_RANGES } from '#uilib/components/ui/TimeRangeControls/TimeRangeControls.constants';
|
|
23
|
+
import {
|
|
24
|
+
Tooltip,
|
|
25
|
+
TooltipContent,
|
|
26
|
+
TooltipTrigger,
|
|
27
|
+
} from '#uilib/components/ui/Tooltip';
|
|
22
28
|
import type { BacktestsComponentPayload } from '@sybilion/platform-sdk';
|
|
29
|
+
import { InfoIcon } from 'lucide-react';
|
|
23
30
|
|
|
24
31
|
import S from './DriversComparisonChart.styl';
|
|
25
32
|
import {
|
|
@@ -282,8 +289,42 @@ export function DriversComparisonChart({
|
|
|
282
289
|
<TableHead className={S.seriesColSeries}>
|
|
283
290
|
Driver name
|
|
284
291
|
</TableHead>
|
|
285
|
-
<TableHead>
|
|
286
|
-
|
|
292
|
+
<TableHead>
|
|
293
|
+
<TableCellValue>
|
|
294
|
+
Importance
|
|
295
|
+
<Tooltip>
|
|
296
|
+
<TooltipTrigger asChild>
|
|
297
|
+
<span>
|
|
298
|
+
<InfoIcon size={16} style={{ cursor: 'help' }} />
|
|
299
|
+
</span>
|
|
300
|
+
</TooltipTrigger>
|
|
301
|
+
<TooltipContent side="top" maxWidth={300}>
|
|
302
|
+
How much this driver contributes to price movements
|
|
303
|
+
in the forecast model. Higher = stronger influence.
|
|
304
|
+
Reflects relative weight of each driver, scored from
|
|
305
|
+
0% to 100%.
|
|
306
|
+
</TooltipContent>
|
|
307
|
+
</Tooltip>
|
|
308
|
+
</TableCellValue>
|
|
309
|
+
</TableHead>
|
|
310
|
+
<TableHead>
|
|
311
|
+
<TableCellValue>
|
|
312
|
+
Lag
|
|
313
|
+
<Tooltip>
|
|
314
|
+
<TooltipTrigger asChild>
|
|
315
|
+
<span>
|
|
316
|
+
<InfoIcon size={16} style={{ cursor: 'help' }} />
|
|
317
|
+
</span>
|
|
318
|
+
</TooltipTrigger>
|
|
319
|
+
<TooltipContent side="top" maxWidth={300}>
|
|
320
|
+
Lag shows how far ahead this driver predicts price
|
|
321
|
+
movement. A range (e.g. 9–12 months) means the
|
|
322
|
+
predictive signal is strongest somewhere in that
|
|
323
|
+
window — not at a single fixed point.
|
|
324
|
+
</TooltipContent>
|
|
325
|
+
</Tooltip>
|
|
326
|
+
</TableCellValue>
|
|
327
|
+
</TableHead>
|
|
287
328
|
</TableRow>
|
|
288
329
|
</TableHeader>
|
|
289
330
|
<TableBody>
|