@sybilion/uilib 1.3.23 → 1.3.26

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 (52) hide show
  1. package/dist/esm/components/ui/TextWithDeferTooltip/TextWithDeferTooltip.js +1 -25
  2. package/dist/esm/components/ui/Tooltip/Tooltip.js +92 -7
  3. package/dist/esm/components/ui/Tooltip/Tooltip.styl.js +2 -2
  4. package/dist/esm/components/widgets/DriversComparisonChart/DriversComparisonChart.js +1 -2
  5. package/dist/esm/components/widgets/PerformanceChart/HorizonsSelector/HorizonsSelector.js +34 -0
  6. package/dist/esm/components/widgets/PerformanceChart/HorizonsSelector/HorizonsSelector.styl.js +7 -0
  7. package/dist/esm/components/widgets/PerformanceChart/PerformanceChart.constants.js +17 -0
  8. package/dist/esm/components/widgets/PerformanceChart/PerformanceChart.js +807 -0
  9. package/dist/esm/components/widgets/PerformanceChart/PerformanceChart.styl.js +7 -0
  10. package/dist/esm/components/widgets/PerformanceChart/PerformanceTable.js +130 -0
  11. package/dist/esm/components/widgets/PerformanceChart/PerformanceUnderChartLegend/PerformanceUnderChartLegend.js +20 -0
  12. package/dist/esm/components/widgets/PerformanceChart/PerformanceUnderChartLegend/PerformanceUnderChartLegend.styl.js +7 -0
  13. package/dist/esm/components/widgets/PerformanceChart/performanceChart.helpers.js +591 -0
  14. package/dist/esm/components/widgets/PerformanceChart/performanceChartUserSeries.js +109 -0
  15. package/dist/esm/index.js +4 -0
  16. package/dist/esm/types/src/components/ui/Tooltip/Tooltip.d.ts +3 -3
  17. package/dist/esm/types/src/components/ui/Tooltip/Tooltip.types.d.ts +1 -0
  18. package/dist/esm/types/src/components/widgets/PerformanceChart/HorizonsSelector/HorizonsSelector.d.ts +7 -0
  19. package/dist/esm/types/src/components/widgets/PerformanceChart/PerformanceChart.constants.d.ts +3 -0
  20. package/dist/esm/types/src/components/widgets/PerformanceChart/PerformanceChart.d.ts +54 -0
  21. package/dist/esm/types/src/components/widgets/PerformanceChart/PerformanceTable.d.ts +31 -0
  22. package/dist/esm/types/src/components/widgets/PerformanceChart/PerformanceUnderChartLegend/PerformanceUnderChartLegend.d.ts +20 -0
  23. package/dist/esm/types/src/components/widgets/PerformanceChart/index.d.ts +4 -0
  24. package/dist/esm/types/src/components/widgets/PerformanceChart/performanceChart.helpers.d.ts +212 -0
  25. package/dist/esm/types/src/components/widgets/PerformanceChart/performanceChartUserSeries.d.ts +20 -0
  26. package/dist/esm/types/src/docs/pages/PerformanceChartPage.d.ts +1 -0
  27. package/dist/esm/types/src/index.d.ts +1 -0
  28. package/package.json +1 -1
  29. package/src/components/ui/TextWithDeferTooltip/TextWithDeferTooltip.tsx +5 -37
  30. package/src/components/ui/Tooltip/Tooltip.styl +12 -0
  31. package/src/components/ui/Tooltip/Tooltip.styl.d.ts +1 -0
  32. package/src/components/ui/Tooltip/Tooltip.tsx +156 -8
  33. package/src/components/ui/Tooltip/Tooltip.types.ts +1 -0
  34. package/src/components/widgets/PerformanceChart/HorizonsSelector/HorizonsSelector.styl +25 -0
  35. package/src/components/widgets/PerformanceChart/HorizonsSelector/HorizonsSelector.styl.d.ts +11 -0
  36. package/src/components/widgets/PerformanceChart/HorizonsSelector/HorizonsSelector.tsx +67 -0
  37. package/src/components/widgets/PerformanceChart/PerformanceChart.constants.ts +17 -0
  38. package/src/components/widgets/PerformanceChart/PerformanceChart.styl +194 -0
  39. package/src/components/widgets/PerformanceChart/PerformanceChart.styl.d.ts +30 -0
  40. package/src/components/widgets/PerformanceChart/PerformanceChart.tsx +1251 -0
  41. package/src/components/widgets/PerformanceChart/PerformanceTable.tsx +381 -0
  42. package/src/components/widgets/PerformanceChart/PerformanceUnderChartLegend/PerformanceUnderChartLegend.styl +49 -0
  43. package/src/components/widgets/PerformanceChart/PerformanceUnderChartLegend/PerformanceUnderChartLegend.styl.d.ts +12 -0
  44. package/src/components/widgets/PerformanceChart/PerformanceUnderChartLegend/PerformanceUnderChartLegend.tsx +83 -0
  45. package/src/components/widgets/PerformanceChart/index.ts +28 -0
  46. package/src/components/widgets/PerformanceChart/performanceChart.helpers.ts +790 -0
  47. package/src/components/widgets/PerformanceChart/performanceChartUserSeries.ts +149 -0
  48. package/src/docs/pages/PerformanceChartPage.tsx +211 -0
  49. package/src/docs/pages/TextWithDeferTooltipPage.tsx +26 -10
  50. package/src/docs/pages/TooltipPage.tsx +30 -0
  51. package/src/docs/registry.ts +6 -0
  52. package/src/index.ts +1 -0
@@ -1,4 +1,13 @@
1
1
  import cn from 'classnames';
2
+ import {
3
+ type Ref,
4
+ createContext,
5
+ useCallback,
6
+ useContext,
7
+ useLayoutEffect,
8
+ useMemo,
9
+ useRef,
10
+ } from 'react';
2
11
 
3
12
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
4
13
 
@@ -10,6 +19,83 @@ import type {
10
19
  TooltipTriggerProps,
11
20
  } from './Tooltip.types';
12
21
 
22
+ type TooltipContextValue = {
23
+ triggerRef: React.RefObject<HTMLElement | null>;
24
+ };
25
+
26
+ const TooltipContext = createContext<TooltipContextValue | null>(null);
27
+
28
+ function composeRefs<T>(...refs: (Ref<T> | undefined)[]) {
29
+ return (node: T | null) => {
30
+ for (const ref of refs) {
31
+ if (typeof ref === 'function') {
32
+ ref(node);
33
+ } else if (ref) {
34
+ ref.current = node;
35
+ }
36
+ }
37
+ };
38
+ }
39
+
40
+ function getPopperWrapper(contentEl: HTMLElement): HTMLElement | null {
41
+ const wrapper = contentEl.parentElement;
42
+ if (!wrapper?.hasAttribute('data-radix-popper-content-wrapper')) return null;
43
+
44
+ return wrapper;
45
+ }
46
+
47
+ const OVER_TRIGGER_OFFSET = { left: -10, top: -6 };
48
+
49
+ function applyOverTriggerStyles(
50
+ contentEl: HTMLElement,
51
+ triggerEl: HTMLElement,
52
+ ) {
53
+ const wrapper = getPopperWrapper(contentEl);
54
+ if (!wrapper) return;
55
+
56
+ const rect = triggerEl.getBoundingClientRect();
57
+ const computed = window.getComputedStyle(triggerEl);
58
+
59
+ wrapper.style.setProperty('position', 'fixed', 'important');
60
+ wrapper.style.setProperty(
61
+ 'left',
62
+ `${rect.left + OVER_TRIGGER_OFFSET.left}px`,
63
+ 'important',
64
+ );
65
+ wrapper.style.setProperty(
66
+ 'top',
67
+ `${rect.top + OVER_TRIGGER_OFFSET.top}px`,
68
+ 'important',
69
+ );
70
+ wrapper.style.setProperty('transform', 'none', 'important');
71
+ wrapper.style.setProperty('min-width', '0', 'important');
72
+ wrapper.style.setProperty('width', `${rect.width}px`, 'important');
73
+
74
+ contentEl.style.width = '100%';
75
+ contentEl.style.boxSizing = 'border-box';
76
+ contentEl.style.fontSize = computed.fontSize;
77
+ contentEl.style.lineHeight = computed.lineHeight;
78
+ }
79
+
80
+ function clearOverTriggerStyles(contentEl: HTMLElement | null) {
81
+ if (!contentEl) return;
82
+
83
+ const wrapper = getPopperWrapper(contentEl);
84
+ if (!wrapper) return;
85
+
86
+ wrapper.style.removeProperty('position');
87
+ wrapper.style.removeProperty('left');
88
+ wrapper.style.removeProperty('top');
89
+ wrapper.style.removeProperty('transform');
90
+ wrapper.style.removeProperty('min-width');
91
+ wrapper.style.removeProperty('width');
92
+
93
+ contentEl.style.removeProperty('width');
94
+ contentEl.style.removeProperty('box-sizing');
95
+ contentEl.style.removeProperty('font-size');
96
+ contentEl.style.removeProperty('line-height');
97
+ }
98
+
13
99
  function TooltipProvider({
14
100
  delayDuration = 0,
15
101
  ...props
@@ -23,16 +109,31 @@ function TooltipProvider({
23
109
  );
24
110
  }
25
111
 
26
- function Tooltip(props: TooltipProps) {
112
+ function Tooltip({ children, ...props }: TooltipProps) {
113
+ const triggerRef = useRef<HTMLElement | null>(null);
114
+ const contextValue = useMemo(() => ({ triggerRef }), []);
115
+
27
116
  return (
28
117
  <TooltipProvider>
29
- <TooltipPrimitive.Root data-slot="tooltip" {...props} />
118
+ <TooltipContext.Provider value={contextValue}>
119
+ <TooltipPrimitive.Root data-slot="tooltip" {...props}>
120
+ {children}
121
+ </TooltipPrimitive.Root>
122
+ </TooltipContext.Provider>
30
123
  </TooltipProvider>
31
124
  );
32
125
  }
33
126
 
34
- function TooltipTrigger({ ...props }: TooltipTriggerProps) {
35
- return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
127
+ function TooltipTrigger({ ref, ...props }: TooltipTriggerProps) {
128
+ const context = useContext(TooltipContext);
129
+
130
+ return (
131
+ <TooltipPrimitive.Trigger
132
+ ref={context ? composeRefs(context.triggerRef, ref) : ref}
133
+ data-slot="tooltip-trigger"
134
+ {...props}
135
+ />
136
+ );
36
137
  }
37
138
 
38
139
  function TooltipContent({
@@ -40,22 +141,69 @@ function TooltipContent({
40
141
  sideOffset = 0,
41
142
  children,
42
143
  maxWidth,
144
+ overTrigger = false,
145
+ align,
146
+ side,
147
+ avoidCollisions,
148
+ style: styleProp,
43
149
  ...props
44
150
  }: TooltipContentProps) {
45
- const style = { ...props.style };
151
+ const context = useContext(TooltipContext);
152
+ const contentRef = useRef<HTMLDivElement | null>(null);
153
+
154
+ const updateOverTriggerPosition = useCallback(() => {
155
+ const triggerEl = context?.triggerRef.current;
156
+ const contentEl = contentRef.current;
157
+ if (!overTrigger || !triggerEl || !contentEl) return;
158
+
159
+ applyOverTriggerStyles(contentEl, triggerEl);
160
+ }, [context, overTrigger]);
161
+
162
+ useLayoutEffect(() => {
163
+ if (!overTrigger) return;
164
+
165
+ let frameId = 0;
166
+
167
+ const tick = () => {
168
+ updateOverTriggerPosition();
169
+ frameId = requestAnimationFrame(tick);
170
+ };
171
+
172
+ tick();
173
+
174
+ window.addEventListener('scroll', updateOverTriggerPosition, true);
175
+ window.addEventListener('resize', updateOverTriggerPosition);
176
+
177
+ return () => {
178
+ cancelAnimationFrame(frameId);
179
+ window.removeEventListener('scroll', updateOverTriggerPosition, true);
180
+ window.removeEventListener('resize', updateOverTriggerPosition);
181
+ clearOverTriggerStyles(contentRef.current);
182
+ };
183
+ }, [overTrigger, updateOverTriggerPosition]);
184
+
185
+ const style = { ...styleProp };
46
186
  if (maxWidth) style.maxWidth = `${maxWidth}px`;
47
187
 
48
188
  return (
49
189
  <TooltipPrimitive.Portal>
50
190
  <TooltipPrimitive.Content
191
+ ref={contentRef}
51
192
  data-slot="tooltip-content"
52
- sideOffset={sideOffset}
53
- className={cn(S.tooltipContent, className)}
193
+ side={overTrigger ? 'bottom' : side}
194
+ align={overTrigger ? 'start' : align}
195
+ sideOffset={overTrigger ? 0 : sideOffset}
196
+ avoidCollisions={overTrigger ? false : avoidCollisions}
197
+ className={cn(
198
+ S.tooltipContent,
199
+ overTrigger && S.tooltipContentOverTrigger,
200
+ className,
201
+ )}
54
202
  {...props}
55
203
  style={style}
56
204
  >
57
205
  {children}
58
- <TooltipPrimitive.Arrow className={S.tooltipArrow} />
206
+ {!overTrigger && <TooltipPrimitive.Arrow className={S.tooltipArrow} />}
59
207
  </TooltipPrimitive.Content>
60
208
  </TooltipPrimitive.Portal>
61
209
  );
@@ -20,4 +20,5 @@ export interface TooltipContentProps extends React.ComponentProps<
20
20
  className?: string;
21
21
  sideOffset?: number;
22
22
  maxWidth?: number;
23
+ overTrigger?: boolean;
23
24
  }
@@ -0,0 +1,25 @@
1
+ @import '../../../../lib/theme.styl'
2
+
3
+ .root
4
+ display flex
5
+ align-items center
6
+ justify-content flex-end
7
+ gap var(--p-4)
8
+ flex-grow 1
9
+
10
+ .horizonsLabel
11
+ font-size 14px
12
+ color var(--foreground)
13
+ font-weight 500
14
+
15
+ .horizonsButtons
16
+ display flex
17
+ gap var(--p-2)
18
+
19
+ .horizonButton
20
+ min-width 40px
21
+
22
+ .infoIcon
23
+ margin-left 4px
24
+ font-size 12px
25
+ opacity 0.6
@@ -0,0 +1,11 @@
1
+ // This file is automatically generated.
2
+ // Please do not change this file!
3
+ interface CssExports {
4
+ 'horizonButton': string;
5
+ 'horizonsButtons': string;
6
+ 'horizonsLabel': string;
7
+ 'infoIcon': string;
8
+ 'root': string;
9
+ }
10
+ export const cssExports: CssExports;
11
+ export default cssExports;
@@ -0,0 +1,67 @@
1
+ import { useMemo } from 'react';
2
+
3
+ import { Tabs, TabsList, TabsTrigger } from '#uilib/components/ui/Tabs';
4
+ import { useThrottledCallback } from '#uilib/hooks/useThrottledCallback';
5
+
6
+ import S from './HorizonsSelector.styl';
7
+
8
+ interface HorizonsSelectorProps {
9
+ selectedHorizon: string;
10
+ onHorizonChange: (horizon: string) => void;
11
+ availableHorizons: string[];
12
+ }
13
+
14
+ export function HorizonsSelector({
15
+ selectedHorizon,
16
+ onHorizonChange,
17
+ availableHorizons,
18
+ }: HorizonsSelectorProps) {
19
+ const horizons = useMemo(() => {
20
+ return availableHorizons
21
+ .sort((a, b) => {
22
+ // Sort by horizon number: horizon_1, horizon_2, etc.
23
+ const numA = parseInt(a.replace('horizon_', ''), 10);
24
+ const numB = parseInt(b.replace('horizon_', ''), 10);
25
+ return numA - numB;
26
+ })
27
+ .map(horizon => {
28
+ const num = horizon.replace('horizon_', '');
29
+ return {
30
+ value: horizon,
31
+ label: num,
32
+ };
33
+ });
34
+ }, [availableHorizons]);
35
+
36
+ const onChangeThrottled = useThrottledCallback(onHorizonChange, 300);
37
+
38
+ if (horizons.length === 0) {
39
+ return null;
40
+ }
41
+
42
+ return (
43
+ <div className={S.root}>
44
+ <span className={S.horizonsLabel}>Planning horizon in months:</span>
45
+ <Tabs
46
+ value={selectedHorizon}
47
+ onValueChange={onHorizonChange}
48
+ variant="button"
49
+ >
50
+ <TabsList>
51
+ {horizons.map(horizon => (
52
+ <TabsTrigger
53
+ key={horizon.value}
54
+ value={horizon.value}
55
+ onPointerMove={() => {
56
+ if (selectedHorizon !== horizon.value)
57
+ onChangeThrottled(horizon.value);
58
+ }}
59
+ >
60
+ {horizon.label}
61
+ </TabsTrigger>
62
+ ))}
63
+ </TabsList>
64
+ </Tabs>
65
+ </div>
66
+ );
67
+ }
@@ -0,0 +1,17 @@
1
+ export const METRICS_KEY = '24m' as const;
2
+ export const MONTHS_24 = 24;
3
+
4
+ export const MONTH_NAMES = [
5
+ 'Jan',
6
+ 'Feb',
7
+ 'Mar',
8
+ 'Apr',
9
+ 'May',
10
+ 'Jun',
11
+ 'Jul',
12
+ 'Aug',
13
+ 'Sep',
14
+ 'Oct',
15
+ 'Nov',
16
+ 'Dec',
17
+ ];
@@ -0,0 +1,194 @@
1
+ @import '../../../lib/theme.styl'
2
+
3
+ .root
4
+ display flex
5
+ flex-direction column
6
+ width 100%
7
+ flex 1
8
+ min-height 0
9
+ transition opacity 300ms ease-out
10
+
11
+ .viewTabs
12
+ display flex
13
+ flex-direction column
14
+ width 100%
15
+ flex 1
16
+ min-height 0
17
+
18
+ .toolbarRow
19
+ display flex
20
+ flex-direction row
21
+ align-items center
22
+ flex-wrap wrap
23
+ gap var(--p-4)
24
+ margin-bottom var(--p-4)
25
+ width 100%
26
+
27
+ .toolBarRight
28
+ flex-grow 1
29
+ display flex
30
+ align-items center
31
+ justify-content flex-end
32
+ gap var(--p-4)
33
+
34
+ .viewTabsList
35
+ display inline-flex
36
+ align-items center
37
+ flex-wrap wrap
38
+ gap var(--p-2)
39
+ margin-bottom 0
40
+ background-color transparent
41
+ flex-shrink 0
42
+
43
+ .perHorizonChartWrap
44
+ position relative
45
+ width 100%
46
+ min-height var(--chart-height)
47
+
48
+ .perHorizonChartWrapLoading
49
+ .chartContainer
50
+ opacity 0.3
51
+ pointer-events none
52
+
53
+ :global(.recharts-line-dots)
54
+ display none
55
+
56
+ .performanceLoadingLayer
57
+ position absolute
58
+ inset 0
59
+ z-index 10
60
+ pointer-events none
61
+
62
+ .chartEmptyOverlay
63
+ position absolute
64
+ inset 0
65
+ z-index 11
66
+ display flex
67
+ align-items center
68
+ justify-content center
69
+ padding var(--p-6)
70
+ pointer-events none
71
+
72
+ a
73
+ pointer-events auto
74
+
75
+ .performanceLoadingText
76
+ font-size 16px
77
+ font-weight 500
78
+ color var(--foreground)
79
+
80
+ .loading
81
+ opacity 0.5
82
+
83
+ .loaded
84
+ opacity 1
85
+
86
+ .chartContainer
87
+ margin-left calc(-1 * var(--page-x-padding) + 26px)
88
+ width calc(100% + 60px)
89
+ max-width @width
90
+ transition opacity 300ms ease-out
91
+
92
+ [data-slot="skeleton"]
93
+ position relative
94
+ height 100% !important
95
+ width 100% !important
96
+ filter opacity(0.6)
97
+
98
+ .loaded &
99
+ opacity 0
100
+
101
+ svg
102
+ overflow visible
103
+
104
+ @media (max-width: unit(MOBILE, 'px'))
105
+ margin-left calc(-1 * var(--page-x-padding) + 10px)
106
+ width calc(100% + 30px)
107
+ max-width @width
108
+
109
+ .isEmpty
110
+ .chartContainer
111
+ opacity 0.3
112
+ pointer-events none
113
+
114
+ :global(.recharts-line-dots)
115
+ display none
116
+
117
+ .tableSection
118
+ --side-margin: calc(var(--page-x-padding) * -1)
119
+ margin var(--p-6) var(--side-margin) 0 var(--side-margin)
120
+ display flex
121
+ flex-direction column
122
+ gap var(--p-4)
123
+
124
+ .tableWrapper
125
+ position relative
126
+
127
+ .tableContainer
128
+ position relative
129
+ width 100%
130
+ overflow-x auto
131
+
132
+ .table
133
+ background var(--card)
134
+ border-radius 14px
135
+ box-shadow inset 0 0 0 1px var(--border)
136
+
137
+ th:first-child
138
+ td:first-child
139
+ padding-left var(--p-10) !important
140
+
141
+ td:last-child
142
+ th:last-child
143
+ padding-right 0 !important
144
+
145
+ .tableCustomDataFooterRow
146
+ td
147
+ text-align center
148
+ border-top 1px solid var(--border)
149
+ vertical-align middle
150
+
151
+ .tableCustomDataFooterCell:first-child:last-child
152
+ padding 0 !important
153
+ text-align center
154
+
155
+ button
156
+ width calc(100% - 2px)
157
+ height calc(100% - 2px)
158
+ border-radius 0
159
+
160
+ .scrollbar
161
+ bottom calc(var(--p-7) * -1 + 2px) !important
162
+
163
+ .forecastCell
164
+ display flex
165
+ align-items center
166
+ gap 8px
167
+
168
+ .colorCircle
169
+ width 5px
170
+ height 20px
171
+ border-radius 10px
172
+ flex-shrink 0
173
+ margin-left -10px
174
+
175
+ .emptyState
176
+ position absolute
177
+ z-index 1
178
+ top calc(50% - 4em)
179
+ left 0
180
+ width 100%
181
+ display block
182
+ text-align center
183
+ padding var(--p-4) var(--p-10)
184
+ color var(--muted-foreground)
185
+
186
+ .performanceLoadingLayer &
187
+ color var(--foreground)
188
+
189
+ a
190
+ padding 0 var(--p-1)
191
+ text-decoration underline
192
+
193
+ &:hover
194
+ color var(--sb-cyan-400)
@@ -0,0 +1,30 @@
1
+ // This file is automatically generated.
2
+ // Please do not change this file!
3
+ interface CssExports {
4
+ 'chartContainer': string;
5
+ 'chartEmptyOverlay': string;
6
+ 'colorCircle': string;
7
+ 'emptyState': string;
8
+ 'forecastCell': string;
9
+ 'isEmpty': string;
10
+ 'loaded': string;
11
+ 'loading': string;
12
+ 'perHorizonChartWrap': string;
13
+ 'perHorizonChartWrapLoading': string;
14
+ 'performanceLoadingLayer': string;
15
+ 'performanceLoadingText': string;
16
+ 'root': string;
17
+ 'scrollbar': string;
18
+ 'table': string;
19
+ 'tableContainer': string;
20
+ 'tableCustomDataFooterCell': string;
21
+ 'tableCustomDataFooterRow': string;
22
+ 'tableSection': string;
23
+ 'tableWrapper': string;
24
+ 'toolBarRight': string;
25
+ 'toolbarRow': string;
26
+ 'viewTabs': string;
27
+ 'viewTabsList': string;
28
+ }
29
+ export const cssExports: CssExports;
30
+ export default cssExports;