panelui-native 0.22.6 → 0.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/lib/module/components/accordion/index.js +8 -8
- package/lib/module/components/accordion/index.js.map +1 -1
- package/lib/module/components/alert/index.js +2 -2
- package/lib/module/components/alert/index.js.map +1 -1
- package/lib/module/components/area-chart/index.js +958 -0
- package/lib/module/components/area-chart/index.js.map +1 -0
- package/lib/module/components/attachment/index.js +2 -1
- package/lib/module/components/attachment/index.js.map +1 -1
- package/lib/module/components/avatar/index.js +4 -4
- package/lib/module/components/avatar/index.js.map +1 -1
- package/lib/module/components/badge/index.js +4 -4
- package/lib/module/components/badge/index.js.map +1 -1
- package/lib/module/components/bar-chart/index.js +994 -0
- package/lib/module/components/bar-chart/index.js.map +1 -0
- package/lib/module/components/bottom-sheet/index.js +213 -24
- package/lib/module/components/bottom-sheet/index.js.map +1 -1
- package/lib/module/components/breadcrumb/index.js +275 -0
- package/lib/module/components/breadcrumb/index.js.map +1 -0
- package/lib/module/components/button/index.js +11 -6
- package/lib/module/components/button/index.js.map +1 -1
- package/lib/module/components/calendar/index.js +557 -0
- package/lib/module/components/calendar/index.js.map +1 -0
- package/lib/module/components/carousel/index.js +680 -0
- package/lib/module/components/carousel/index.js.map +1 -0
- package/lib/module/components/chip/index.js +6 -6
- package/lib/module/components/chip/index.js.map +1 -1
- package/lib/module/components/date-picker/index.js +158 -0
- package/lib/module/components/date-picker/index.js.map +1 -0
- package/lib/module/components/dialog/index.js +7 -2
- package/lib/module/components/dialog/index.js.map +1 -1
- package/lib/module/components/direction/index.js +15 -20
- package/lib/module/components/direction/index.js.map +1 -1
- package/lib/module/components/empty-state/index.js +2 -2
- package/lib/module/components/empty-state/index.js.map +1 -1
- package/lib/module/components/field/index.js +5 -5
- package/lib/module/components/field/index.js.map +1 -1
- package/lib/module/components/flow/index.js +3 -3
- package/lib/module/components/flow/index.js.map +1 -1
- package/lib/module/components/frame/index.js +22 -5
- package/lib/module/components/frame/index.js.map +1 -1
- package/lib/module/components/heatmap-chart/index.js +28 -8
- package/lib/module/components/heatmap-chart/index.js.map +1 -1
- package/lib/module/components/input-group/index.js +5 -4
- package/lib/module/components/input-group/index.js.map +1 -1
- package/lib/module/components/item/index.js +9 -9
- package/lib/module/components/item/index.js.map +1 -1
- package/lib/module/components/label/index.js +4 -4
- package/lib/module/components/label/index.js.map +1 -1
- package/lib/module/components/line-chart/index.js +165 -184
- package/lib/module/components/line-chart/index.js.map +1 -1
- package/lib/module/components/loader/index.js +758 -0
- package/lib/module/components/loader/index.js.map +1 -0
- package/lib/module/components/map/basemap.js +265 -0
- package/lib/module/components/map/basemap.js.map +1 -0
- package/lib/module/components/map/index.js +850 -0
- package/lib/module/components/map/index.js.map +1 -0
- package/lib/module/components/map/maplibre.js +87 -0
- package/lib/module/components/map/maplibre.js.map +1 -0
- package/lib/module/components/marker/index.js +3 -3
- package/lib/module/components/marker/index.js.map +1 -1
- package/lib/module/components/message/index.js +15 -14
- package/lib/module/components/message/index.js.map +1 -1
- package/lib/module/components/message-scroller/index.js +5 -4
- package/lib/module/components/message-scroller/index.js.map +1 -1
- package/lib/module/components/otp-input/index.js +2 -2
- package/lib/module/components/popover/index.js +39 -7
- package/lib/module/components/popover/index.js.map +1 -1
- package/lib/module/components/progress/index.js +8 -2
- package/lib/module/components/progress/index.js.map +1 -1
- package/lib/module/components/radio-group/index.js +2 -2
- package/lib/module/components/radio-group/index.js.map +1 -1
- package/lib/module/components/ring-chart/index.js +423 -0
- package/lib/module/components/ring-chart/index.js.map +1 -0
- package/lib/module/components/section-rail/index.js +6 -6
- package/lib/module/components/section-rail/index.js.map +1 -1
- package/lib/module/components/select/index.js +10 -4
- package/lib/module/components/select/index.js.map +1 -1
- package/lib/module/components/separator/index.js +76 -22
- package/lib/module/components/separator/index.js.map +1 -1
- package/lib/module/components/shimmer/index.js +10 -1
- package/lib/module/components/shimmer/index.js.map +1 -1
- package/lib/module/components/slider/index.js +19 -5
- package/lib/module/components/slider/index.js.map +1 -1
- package/lib/module/components/steps/index.js +5 -5
- package/lib/module/components/steps/index.js.map +1 -1
- package/lib/module/components/switch/index.js +6 -1
- package/lib/module/components/switch/index.js.map +1 -1
- package/lib/module/components/tabs/index.js +7 -7
- package/lib/module/components/tabs/index.js.map +1 -1
- package/lib/module/components/timeline/index.js +4 -4
- package/lib/module/components/timeline/index.js.map +1 -1
- package/lib/module/components/toast/index.js +2 -2
- package/lib/module/components/toast/index.js.map +1 -1
- package/lib/module/components/toggle-button/index.js +5 -5
- package/lib/module/components/toggle-button/index.js.map +1 -1
- package/lib/module/components/tooltip/index.js +492 -0
- package/lib/module/components/tooltip/index.js.map +1 -0
- package/lib/module/components/typography/index.js +7 -2
- package/lib/module/components/typography/index.js.map +1 -1
- package/lib/module/hooks/index.js +1 -0
- package/lib/module/hooks/index.js.map +1 -1
- package/lib/module/hooks/use-back-handler.js +37 -0
- package/lib/module/hooks/use-back-handler.js.map +1 -0
- package/lib/module/hooks/use-direction.js +43 -0
- package/lib/module/hooks/use-direction.js.map +1 -0
- package/lib/module/icons/index.js +180 -2
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +12 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/primitives/keyboard-avoider.js +2 -1
- package/lib/module/primitives/keyboard-avoider.js.map +1 -1
- package/lib/module/primitives/text.js +55 -11
- package/lib/module/primitives/text.js.map +1 -1
- package/lib/module/utils/chart.js +279 -0
- package/lib/module/utils/chart.js.map +1 -0
- package/lib/module/utils/date.js +386 -0
- package/lib/module/utils/date.js.map +1 -0
- package/lib/typescript/src/components/accordion/index.d.ts.map +1 -1
- package/lib/typescript/src/components/alert/index.d.ts.map +1 -1
- package/lib/typescript/src/components/area-chart/index.d.ts +237 -0
- package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/components/attachment/index.d.ts.map +1 -1
- package/lib/typescript/src/components/bar-chart/index.d.ts +270 -0
- package/lib/typescript/src/components/bar-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/components/bottom-sheet/index.d.ts +75 -4
- package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
- package/lib/typescript/src/components/breadcrumb/index.d.ts +100 -0
- package/lib/typescript/src/components/breadcrumb/index.d.ts.map +1 -0
- package/lib/typescript/src/components/button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/calendar/index.d.ts +108 -0
- package/lib/typescript/src/components/calendar/index.d.ts.map +1 -0
- package/lib/typescript/src/components/carousel/index.d.ts +179 -0
- package/lib/typescript/src/components/carousel/index.d.ts.map +1 -0
- package/lib/typescript/src/components/chip/index.d.ts.map +1 -1
- package/lib/typescript/src/components/date-picker/index.d.ts +91 -0
- package/lib/typescript/src/components/date-picker/index.d.ts.map +1 -0
- package/lib/typescript/src/components/dialog/index.d.ts.map +1 -1
- package/lib/typescript/src/components/direction/index.d.ts +11 -10
- package/lib/typescript/src/components/direction/index.d.ts.map +1 -1
- package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -1
- package/lib/typescript/src/components/field/index.d.ts.map +1 -1
- package/lib/typescript/src/components/frame/index.d.ts +17 -0
- package/lib/typescript/src/components/frame/index.d.ts.map +1 -1
- package/lib/typescript/src/components/heatmap-chart/index.d.ts +22 -3
- package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/input-group/index.d.ts.map +1 -1
- package/lib/typescript/src/components/item/index.d.ts.map +1 -1
- package/lib/typescript/src/components/label/index.d.ts.map +1 -1
- package/lib/typescript/src/components/line-chart/index.d.ts +42 -2
- package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/loader/index.d.ts +38 -0
- package/lib/typescript/src/components/loader/index.d.ts.map +1 -0
- package/lib/typescript/src/components/map/basemap.d.ts +76 -0
- package/lib/typescript/src/components/map/basemap.d.ts.map +1 -0
- package/lib/typescript/src/components/map/index.d.ts +367 -0
- package/lib/typescript/src/components/map/index.d.ts.map +1 -0
- package/lib/typescript/src/components/map/maplibre.d.ts +193 -0
- package/lib/typescript/src/components/map/maplibre.d.ts.map +1 -0
- package/lib/typescript/src/components/marker/index.d.ts.map +1 -1
- package/lib/typescript/src/components/message/index.d.ts.map +1 -1
- package/lib/typescript/src/components/message-scroller/index.d.ts.map +1 -1
- package/lib/typescript/src/components/popover/index.d.ts.map +1 -1
- package/lib/typescript/src/components/progress/index.d.ts.map +1 -1
- package/lib/typescript/src/components/ring-chart/index.d.ts +156 -0
- package/lib/typescript/src/components/ring-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/components/select/index.d.ts.map +1 -1
- package/lib/typescript/src/components/separator/index.d.ts +36 -1
- package/lib/typescript/src/components/separator/index.d.ts.map +1 -1
- package/lib/typescript/src/components/shimmer/index.d.ts.map +1 -1
- package/lib/typescript/src/components/slider/index.d.ts.map +1 -1
- package/lib/typescript/src/components/steps/index.d.ts.map +1 -1
- package/lib/typescript/src/components/switch/index.d.ts.map +1 -1
- package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
- package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
- package/lib/typescript/src/components/toast/index.d.ts.map +1 -1
- package/lib/typescript/src/components/toggle-button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/tooltip/index.d.ts +125 -0
- package/lib/typescript/src/components/tooltip/index.d.ts.map +1 -0
- package/lib/typescript/src/components/typography/index.d.ts +6 -0
- package/lib/typescript/src/components/typography/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/index.d.ts +1 -0
- package/lib/typescript/src/hooks/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/use-back-handler.d.ts +19 -0
- package/lib/typescript/src/hooks/use-back-handler.d.ts.map +1 -0
- package/lib/typescript/src/hooks/use-direction.d.ts +20 -0
- package/lib/typescript/src/hooks/use-direction.d.ts.map +1 -0
- package/lib/typescript/src/icons/index.d.ts +8 -0
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +14 -4
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/primitives/keyboard-avoider.d.ts.map +1 -1
- package/lib/typescript/src/primitives/text.d.ts +20 -0
- package/lib/typescript/src/primitives/text.d.ts.map +1 -1
- package/lib/typescript/src/utils/chart.d.ts +96 -0
- package/lib/typescript/src/utils/chart.d.ts.map +1 -0
- package/lib/typescript/src/utils/date.d.ts +116 -0
- package/lib/typescript/src/utils/date.d.ts.map +1 -0
- package/package.json +9 -1
- package/src/components/accordion/index.tsx +8 -12
- package/src/components/alert/index.tsx +2 -2
- package/src/components/area-chart/index.tsx +1147 -0
- package/src/components/attachment/index.tsx +2 -2
- package/src/components/avatar/index.tsx +4 -4
- package/src/components/badge/index.tsx +6 -6
- package/src/components/bar-chart/index.tsx +1208 -0
- package/src/components/bottom-sheet/index.tsx +282 -21
- package/src/components/breadcrumb/index.tsx +363 -0
- package/src/components/button/index.tsx +12 -8
- package/src/components/calendar/index.tsx +703 -0
- package/src/components/carousel/index.tsx +788 -0
- package/src/components/chip/index.tsx +6 -8
- package/src/components/date-picker/index.tsx +237 -0
- package/src/components/dialog/index.tsx +7 -2
- package/src/components/direction/index.tsx +22 -22
- package/src/components/empty-state/index.tsx +2 -2
- package/src/components/field/index.tsx +3 -5
- package/src/components/flow/index.tsx +3 -3
- package/src/components/frame/index.tsx +22 -7
- package/src/components/heatmap-chart/index.tsx +58 -11
- package/src/components/input-group/index.tsx +5 -4
- package/src/components/item/index.tsx +9 -9
- package/src/components/label/index.tsx +2 -2
- package/src/components/line-chart/index.tsx +194 -223
- package/src/components/loader/index.tsx +812 -0
- package/src/components/map/basemap.ts +304 -0
- package/src/components/map/index.tsx +1116 -0
- package/src/components/map/maplibre.ts +201 -0
- package/src/components/marker/index.tsx +3 -3
- package/src/components/message/index.tsx +15 -14
- package/src/components/message-scroller/index.tsx +5 -4
- package/src/components/otp-input/index.tsx +2 -2
- package/src/components/popover/index.tsx +43 -7
- package/src/components/progress/index.tsx +10 -2
- package/src/components/radio-group/index.tsx +2 -2
- package/src/components/ring-chart/index.tsx +543 -0
- package/src/components/section-rail/index.tsx +6 -6
- package/src/components/select/index.tsx +10 -4
- package/src/components/separator/index.tsx +77 -19
- package/src/components/shimmer/index.tsx +10 -1
- package/src/components/slider/index.tsx +20 -5
- package/src/components/steps/index.tsx +5 -5
- package/src/components/switch/index.tsx +6 -1
- package/src/components/tabs/index.tsx +7 -9
- package/src/components/timeline/index.tsx +4 -4
- package/src/components/toast/index.tsx +2 -2
- package/src/components/toggle-button/index.tsx +5 -7
- package/src/components/tooltip/index.tsx +610 -0
- package/src/components/typography/index.tsx +7 -2
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-back-handler.ts +37 -0
- package/src/hooks/use-direction.ts +45 -0
- package/src/icons/index.tsx +111 -0
- package/src/index.ts +129 -0
- package/src/primitives/keyboard-avoider.tsx +2 -1
- package/src/primitives/text.tsx +53 -8
- package/src/utils/chart.ts +355 -0
- package/src/utils/date.ts +446 -0
|
@@ -28,7 +28,7 @@ import { createContext, forwardRef, useContext, type ReactNode } from 'react';
|
|
|
28
28
|
import { View, type ViewProps } from 'react-native';
|
|
29
29
|
import { tv, type VariantProps } from 'tailwind-variants';
|
|
30
30
|
import { AnimatedPressable, type AnimatedPressableProps } from '../../primitives/animated-pressable';
|
|
31
|
-
import { Text, type TextProps } from '../../primitives/text';
|
|
31
|
+
import { Text, type TextProps, textChildren } from '../../primitives/text';
|
|
32
32
|
import { cn } from '../../utils/cn';
|
|
33
33
|
|
|
34
34
|
type ItemSize = 'default' | 'sm' | 'xs';
|
|
@@ -152,7 +152,7 @@ const ItemRoot = forwardRef<View, ItemProps>(
|
|
|
152
152
|
className={root({ className })}
|
|
153
153
|
{...(props as ViewProps)}
|
|
154
154
|
>
|
|
155
|
-
{children}
|
|
155
|
+
{textChildren(children)}
|
|
156
156
|
</View>
|
|
157
157
|
) : (
|
|
158
158
|
<AnimatedPressable
|
|
@@ -164,7 +164,7 @@ const ItemRoot = forwardRef<View, ItemProps>(
|
|
|
164
164
|
className={root({ className })}
|
|
165
165
|
{...props}
|
|
166
166
|
>
|
|
167
|
-
{children}
|
|
167
|
+
{textChildren(children)}
|
|
168
168
|
</AnimatedPressable>
|
|
169
169
|
);
|
|
170
170
|
|
|
@@ -200,7 +200,7 @@ const ItemGroup = forwardRef<View, ItemGroupProps>(
|
|
|
200
200
|
)}
|
|
201
201
|
{...props}
|
|
202
202
|
>
|
|
203
|
-
{children}
|
|
203
|
+
{textChildren(children)}
|
|
204
204
|
</View>
|
|
205
205
|
)
|
|
206
206
|
);
|
|
@@ -246,7 +246,7 @@ const ItemMedia = forwardRef<View, ItemMediaProps>(
|
|
|
246
246
|
className={mediaVariants({ variant, size: size ?? item.size, className })}
|
|
247
247
|
{...props}
|
|
248
248
|
>
|
|
249
|
-
{children}
|
|
249
|
+
{textChildren(children)}
|
|
250
250
|
</View>
|
|
251
251
|
);
|
|
252
252
|
}
|
|
@@ -277,7 +277,7 @@ const ItemContent = forwardRef<View, ItemContentProps>(
|
|
|
277
277
|
)}
|
|
278
278
|
{...props}
|
|
279
279
|
>
|
|
280
|
-
{children}
|
|
280
|
+
{textChildren(children)}
|
|
281
281
|
</View>
|
|
282
282
|
);
|
|
283
283
|
}
|
|
@@ -326,7 +326,7 @@ const ItemActions = forwardRef<View, ItemActionsProps>(
|
|
|
326
326
|
className={cn('shrink-0 flex-row items-center gap-1.5', className)}
|
|
327
327
|
{...props}
|
|
328
328
|
>
|
|
329
|
-
{children}
|
|
329
|
+
{textChildren(children)}
|
|
330
330
|
</View>
|
|
331
331
|
)
|
|
332
332
|
);
|
|
@@ -349,7 +349,7 @@ const ItemHeader = forwardRef<View, ItemHeaderProps>(
|
|
|
349
349
|
className={cn('w-full flex-row items-center justify-between gap-2', className)}
|
|
350
350
|
{...props}
|
|
351
351
|
>
|
|
352
|
-
{children}
|
|
352
|
+
{textChildren(children)}
|
|
353
353
|
</View>
|
|
354
354
|
)
|
|
355
355
|
);
|
|
@@ -368,7 +368,7 @@ const ItemFooter = forwardRef<View, ItemFooterProps>(
|
|
|
368
368
|
className={cn('w-full flex-row items-center gap-2', className)}
|
|
369
369
|
{...props}
|
|
370
370
|
>
|
|
371
|
-
{children}
|
|
371
|
+
{textChildren(children)}
|
|
372
372
|
</View>
|
|
373
373
|
)
|
|
374
374
|
);
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from 'react';
|
|
17
17
|
import { View, type Text as RNText, type ViewProps } from 'react-native';
|
|
18
18
|
import { tv } from 'tailwind-variants';
|
|
19
|
-
import { Text, type TextProps } from '../../primitives/text';
|
|
19
|
+
import { Text, type TextProps, textChildren } from '../../primitives/text';
|
|
20
20
|
|
|
21
21
|
const labelVariants = tv({
|
|
22
22
|
slots: {
|
|
@@ -86,7 +86,7 @@ const LabelRoot = forwardRef<View, LabelProps>(
|
|
|
86
86
|
{...props}
|
|
87
87
|
>
|
|
88
88
|
{/* Bare strings are the common case — wrap them so callers don't have to. */}
|
|
89
|
-
{
|
|
89
|
+
{textChildren(children, (text) => <LabelText>{text}</LabelText>)}
|
|
90
90
|
</View>
|
|
91
91
|
</LabelContext.Provider>
|
|
92
92
|
);
|
|
@@ -81,6 +81,16 @@ import Svg, {
|
|
|
81
81
|
} from 'react-native-svg';
|
|
82
82
|
import { useCSSVariable } from 'uniwind';
|
|
83
83
|
import { Text } from '../../primitives/text';
|
|
84
|
+
import {
|
|
85
|
+
areaPath,
|
|
86
|
+
columnValues,
|
|
87
|
+
compactNumber,
|
|
88
|
+
linePath,
|
|
89
|
+
useSeriesColor,
|
|
90
|
+
xOf,
|
|
91
|
+
yOf,
|
|
92
|
+
type Plot,
|
|
93
|
+
} from '../../utils/chart';
|
|
84
94
|
import { cn } from '../../utils/cn';
|
|
85
95
|
|
|
86
96
|
const AnimatedPath = Animated.createAnimatedComponent(Path);
|
|
@@ -96,19 +106,12 @@ const PADDING = { top: 12, right: 10, bottom: 22, left: 10 };
|
|
|
96
106
|
* stays a flat list of children instead of two nested slots the caller has to
|
|
97
107
|
* remember the order of.
|
|
98
108
|
*/
|
|
99
|
-
type Layer = 'svg' | 'overlay';
|
|
109
|
+
type Layer = 'svg' | 'overlay' | 'header';
|
|
100
110
|
|
|
101
111
|
export type LineChartStatus = 'loading' | 'ready';
|
|
102
112
|
export type LineChartCurve = 'monotone' | 'linear';
|
|
103
113
|
export type LineChartDatum = Record<string, string | number | null | undefined>;
|
|
104
114
|
|
|
105
|
-
interface Plot {
|
|
106
|
-
width: number;
|
|
107
|
-
height: number;
|
|
108
|
-
left: number;
|
|
109
|
-
top: number;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
115
|
interface LineChartContextValue {
|
|
113
116
|
data: LineChartDatum[];
|
|
114
117
|
xDataKey: string;
|
|
@@ -385,39 +388,43 @@ const LineChartRoot = forwardRef<LineChartHandle, LineChartProps>(function LineC
|
|
|
385
388
|
]
|
|
386
389
|
);
|
|
387
390
|
|
|
388
|
-
const { svg, overlay } = partition(children);
|
|
391
|
+
const { svg, overlay, header } = partition(children);
|
|
389
392
|
|
|
393
|
+
/*
|
|
394
|
+
* Two views, because the header is not part of the plot. `aspectRatio` and
|
|
395
|
+
* the layout measurement belong to the drawing area alone — measured on the
|
|
396
|
+
* outer view they would take in the header too, and the plot would lose as
|
|
397
|
+
* much height as the readout took while still claiming the shape asked for.
|
|
398
|
+
*/
|
|
390
399
|
return (
|
|
391
400
|
<LineChartContext.Provider value={context}>
|
|
392
|
-
<View
|
|
393
|
-
{
|
|
394
|
-
onLayout={onLayout}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
</>
|
|
420
|
-
) : null}
|
|
401
|
+
<View {...props} style={props.style} className={cn('w-full', className)}>
|
|
402
|
+
{header}
|
|
403
|
+
<View onLayout={onLayout} style={{ aspectRatio }} className="w-full">
|
|
404
|
+
{plot.width > 0 ? (
|
|
405
|
+
<>
|
|
406
|
+
<Svg width="100%" height="100%" style={StyleSheet.absoluteFill}>
|
|
407
|
+
<Defs>
|
|
408
|
+
{/*
|
|
409
|
+
* One clip for everything in the plot. Sharing it is what
|
|
410
|
+
* makes the reveal read as the chart arriving, rather than as
|
|
411
|
+
* three separate things animating in at once.
|
|
412
|
+
*/}
|
|
413
|
+
<ClipPath id={clipId}>
|
|
414
|
+
<AnimatedRect
|
|
415
|
+
x={plot.left}
|
|
416
|
+
y={0}
|
|
417
|
+
height={size.height}
|
|
418
|
+
animatedProps={clipProps}
|
|
419
|
+
/>
|
|
420
|
+
</ClipPath>
|
|
421
|
+
</Defs>
|
|
422
|
+
{svg}
|
|
423
|
+
</Svg>
|
|
424
|
+
{overlay}
|
|
425
|
+
</>
|
|
426
|
+
) : null}
|
|
427
|
+
</View>
|
|
421
428
|
</View>
|
|
422
429
|
</LineChartContext.Provider>
|
|
423
430
|
);
|
|
@@ -428,18 +435,19 @@ LineChartRoot.displayName = 'LineChart';
|
|
|
428
435
|
function partition(children: ReactNode) {
|
|
429
436
|
const svg: ReactNode[] = [];
|
|
430
437
|
const overlay: ReactNode[] = [];
|
|
438
|
+
const header: ReactNode[] = [];
|
|
431
439
|
|
|
432
440
|
Children.forEach(children, (child, index) => {
|
|
433
441
|
if (!isValidElement(child)) return;
|
|
434
442
|
const layer = (child.type as { layer?: Layer }).layer ?? 'svg';
|
|
435
|
-
(layer === 'overlay' ? overlay : svg).push(
|
|
443
|
+
(layer === 'header' ? header : layer === 'overlay' ? overlay : svg).push(
|
|
436
444
|
// Children of a `Children.forEach` need keys of their own once they are
|
|
437
445
|
// put into a new array.
|
|
438
446
|
<ChildSlot key={index}>{child}</ChildSlot>
|
|
439
447
|
);
|
|
440
448
|
});
|
|
441
449
|
|
|
442
|
-
return { svg, overlay };
|
|
450
|
+
return { svg, overlay, header };
|
|
443
451
|
}
|
|
444
452
|
|
|
445
453
|
/** Identity wrapper, purely so the partitioned arrays can carry keys. */
|
|
@@ -524,7 +532,7 @@ function LineChartLine({
|
|
|
524
532
|
return () => unregisterSeries(dataKey);
|
|
525
533
|
}, [dataKey, stroke, registerSeries, unregisterSeries]);
|
|
526
534
|
|
|
527
|
-
const values = useMemo(() =>
|
|
535
|
+
const values = useMemo(() => columnValues(data, dataKey), [data, dataKey]);
|
|
528
536
|
const loading = status === 'loading';
|
|
529
537
|
|
|
530
538
|
const animatedProps = useAnimatedProps(() => ({
|
|
@@ -609,7 +617,7 @@ function LineChartArea({ dataKey, color, colorIndex = 1, opacity = 0.18 }: LineC
|
|
|
609
617
|
const fill = useSeriesColor(color, colorIndex);
|
|
610
618
|
const gradientId = useRef(`panelui-area-${Math.random().toString(36).slice(2, 9)}`).current;
|
|
611
619
|
|
|
612
|
-
const values = useMemo(() =>
|
|
620
|
+
const values = useMemo(() => columnValues(data, dataKey), [data, dataKey]);
|
|
613
621
|
const loading = status === 'loading';
|
|
614
622
|
|
|
615
623
|
const animatedProps = useAnimatedProps(() => ({
|
|
@@ -734,20 +742,44 @@ function LineChartXAxis({ ticks = 4, format, className }: LineChartXAxisProps) {
|
|
|
734
742
|
}).filter((label): label is { key: number; text: string } => label !== null);
|
|
735
743
|
}, [data, ticks, format, xDataKey]);
|
|
736
744
|
|
|
745
|
+
/*
|
|
746
|
+
* Each label sits on its own point rather than being spread along the axis.
|
|
747
|
+
* The indices `ticks` picks are not evenly spaced — twelve months shown five
|
|
748
|
+
* at a time gives 0,3,6,8,11 — so spreading them evenly put the ones in
|
|
749
|
+
* between over the wrong part of the line.
|
|
750
|
+
*
|
|
751
|
+
* The box is backed off by half its own width rather than translated by
|
|
752
|
+
* `-50%`, which is not reliable across React Native versions.
|
|
753
|
+
*/
|
|
737
754
|
return (
|
|
738
755
|
<View
|
|
739
|
-
pointerEvents
|
|
740
|
-
|
|
741
|
-
position: 'absolute',
|
|
742
|
-
left: plot.left,
|
|
743
|
-
right: PADDING.right,
|
|
744
|
-
bottom: 0,
|
|
745
|
-
height: PADDING.bottom,
|
|
746
|
-
}}
|
|
747
|
-
className={cn('flex-row items-center justify-between', className)}
|
|
756
|
+
style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}
|
|
757
|
+
className={cn(className)}
|
|
748
758
|
>
|
|
749
759
|
{labels.map((label) => (
|
|
750
|
-
<Text
|
|
760
|
+
<Text
|
|
761
|
+
key={label.key}
|
|
762
|
+
size="xs"
|
|
763
|
+
muted
|
|
764
|
+
numberOfLines={1}
|
|
765
|
+
style={{
|
|
766
|
+
position: 'absolute',
|
|
767
|
+
bottom: 0,
|
|
768
|
+
// Centred on its point, then held inside the chart. The first and
|
|
769
|
+
// last points sit on the plot's own edges, so a box centred on
|
|
770
|
+
// them hangs half its width off the side — the clamp slides those
|
|
771
|
+
// two back in rather than letting the numbers leave the frame.
|
|
772
|
+
left: Math.max(
|
|
773
|
+
0,
|
|
774
|
+
Math.min(
|
|
775
|
+
xOf(label.key, data.length, plot) - POINT_LABEL_WIDTH / 2,
|
|
776
|
+
plot.left + plot.width + PADDING.right - POINT_LABEL_WIDTH
|
|
777
|
+
)
|
|
778
|
+
),
|
|
779
|
+
width: POINT_LABEL_WIDTH,
|
|
780
|
+
textAlign: 'center',
|
|
781
|
+
}}
|
|
782
|
+
>
|
|
751
783
|
{label.text}
|
|
752
784
|
</Text>
|
|
753
785
|
))}
|
|
@@ -893,7 +925,7 @@ function LineChartTooltip({ color, showLabel = true, formatValue, formatX }: Lin
|
|
|
893
925
|
{series.map(([key, seriesColor]) => (
|
|
894
926
|
<TooltipDot
|
|
895
927
|
key={key}
|
|
896
|
-
values={
|
|
928
|
+
values={columnValues(data, key)}
|
|
897
929
|
plot={plot}
|
|
898
930
|
domainMin={domainMin}
|
|
899
931
|
domainMax={domainMax}
|
|
@@ -952,14 +984,17 @@ LineChartTooltip.layer = 'overlay' as Layer;
|
|
|
952
984
|
|
|
953
985
|
const LABEL_WIDTH = 112;
|
|
954
986
|
|
|
955
|
-
/**
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
987
|
+
/**
|
|
988
|
+
* Box each x label is centred in. Wide enough for a short label to sit over
|
|
989
|
+
* its point without the neighbours colliding; a longer one is ellipsised
|
|
990
|
+
* rather than allowed to push the others out of place.
|
|
991
|
+
*/
|
|
992
|
+
const POINT_LABEL_WIDTH = 56;
|
|
993
|
+
|
|
994
|
+
/** Line height of an `xs` label, for centring one on the grid line it names. */
|
|
995
|
+
const AXIS_LABEL_HEIGHT = 16;
|
|
962
996
|
|
|
997
|
+
/** A compact number for the crosshair label: 1204 → "1.2k", 24801 → "24.8k". */
|
|
963
998
|
const DOT = 9;
|
|
964
999
|
|
|
965
1000
|
/** The dot riding one series under the crosshair. */
|
|
@@ -1022,20 +1057,16 @@ export interface LineChartLegendProps extends ViewProps {
|
|
|
1022
1057
|
*/
|
|
1023
1058
|
function LineChartLegend({ className, labels, ...props }: LineChartLegendProps) {
|
|
1024
1059
|
const { series } = useChart('LineChart.Legend');
|
|
1060
|
+
if (!series.length) return null;
|
|
1025
1061
|
|
|
1026
1062
|
return (
|
|
1027
1063
|
<View
|
|
1028
|
-
pointerEvents="none"
|
|
1029
1064
|
className={cn('absolute left-2.5 top-0 flex-row flex-wrap items-center gap-4', className)}
|
|
1030
1065
|
{...props}
|
|
1066
|
+
style={[{ pointerEvents: 'none' }, props.style]}
|
|
1031
1067
|
>
|
|
1032
1068
|
{series.map(([key, color]) => (
|
|
1033
|
-
<
|
|
1034
|
-
<View style={{ backgroundColor: color }} className="h-2 w-2 rounded-full" />
|
|
1035
|
-
<Text size="xs" muted>
|
|
1036
|
-
{labels?.[key] ?? key}
|
|
1037
|
-
</Text>
|
|
1038
|
-
</View>
|
|
1069
|
+
<SeriesSwatch key={key} color={color} label={labels?.[key] ?? key} />
|
|
1039
1070
|
))}
|
|
1040
1071
|
</View>
|
|
1041
1072
|
);
|
|
@@ -1043,177 +1074,117 @@ function LineChartLegend({ className, labels, ...props }: LineChartLegendProps)
|
|
|
1043
1074
|
LineChartLegend.displayName = 'LineChart.Legend';
|
|
1044
1075
|
LineChartLegend.layer = 'overlay' as Layer;
|
|
1045
1076
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
return explicit ?? (typeof token === 'string' ? token : FALLBACK_SERIES[index - 1]!);
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
function numbersOf(data: LineChartDatum[], key: string): (number | null)[] {
|
|
1060
|
-
return data.map((row) => {
|
|
1061
|
-
const value = row[key];
|
|
1062
|
-
return typeof value === 'number' && !Number.isNaN(value) ? value : null;
|
|
1063
|
-
});
|
|
1077
|
+
/** One series' colour and name. Shared by the legend and the header. */
|
|
1078
|
+
function SeriesSwatch({ color, label }: { color: string; label: string }) {
|
|
1079
|
+
return (
|
|
1080
|
+
<View className="flex-row items-center gap-1.5">
|
|
1081
|
+
<View style={{ backgroundColor: color }} className="h-2 w-2 rounded-full" />
|
|
1082
|
+
<Text size="xs" muted>
|
|
1083
|
+
{label}
|
|
1084
|
+
</Text>
|
|
1085
|
+
</View>
|
|
1086
|
+
);
|
|
1064
1087
|
}
|
|
1065
1088
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
return plot.left + (plot.width * index) / (total - 1);
|
|
1070
|
-
}
|
|
1089
|
+
/* -------------------------------------------------------------------------- */
|
|
1090
|
+
/* Header layer */
|
|
1091
|
+
/* -------------------------------------------------------------------------- */
|
|
1071
1092
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1093
|
+
export interface LineChartHeaderProps extends ViewProps {
|
|
1094
|
+
className?: string;
|
|
1095
|
+
/** Small line above the value — what the chart is of. */
|
|
1096
|
+
title?: string;
|
|
1097
|
+
/** The readout. The largest thing on the card, and the first thing read. */
|
|
1098
|
+
value?: string;
|
|
1099
|
+
/** One muted line under the value — a period, a comparison, a total. */
|
|
1100
|
+
caption?: string;
|
|
1101
|
+
/** Prettier names for the series keys, as the legend takes. */
|
|
1102
|
+
labels?: Record<string, string>;
|
|
1103
|
+
/**
|
|
1104
|
+
* Draw a swatch and a name per series along the trailing edge. Prefer this to
|
|
1105
|
+
* `LineChart.Legend` on a chart that has a header: the legend floats over the
|
|
1106
|
+
* plot, where it competes with the lines for the same corner.
|
|
1107
|
+
*/
|
|
1108
|
+
legend?: boolean;
|
|
1109
|
+
/** Trailing slot — a control, a badge, a range picker. Wins over `legend`. */
|
|
1110
|
+
children?: ReactNode;
|
|
1076
1111
|
}
|
|
1077
1112
|
|
|
1078
1113
|
/**
|
|
1079
|
-
*
|
|
1114
|
+
* The strip above the plot: what the chart is of, what it currently reads, and
|
|
1115
|
+
* what the colours mean.
|
|
1080
1116
|
*
|
|
1081
|
-
*
|
|
1082
|
-
*
|
|
1083
|
-
*
|
|
1117
|
+
* It belongs to the chart rather than to the card around it because it is about
|
|
1118
|
+
* the *plot* — the number changes as a finger moves along the line, and the
|
|
1119
|
+
* legend is the series list the chart itself is holding. The card's header is a
|
|
1120
|
+
* caption on the tray the chart sits in; this is the chart introducing itself.
|
|
1084
1121
|
*
|
|
1085
|
-
*
|
|
1086
|
-
*
|
|
1087
|
-
*
|
|
1088
|
-
*
|
|
1089
|
-
* either side of it, and a local peak or trough is given a flat one.
|
|
1122
|
+
* The value is not derived here. A readout that follows the finger belongs to
|
|
1123
|
+
* whoever owns the data — take it from `onActiveIndexChange` and pass the
|
|
1124
|
+
* formatted string down, so one header can show a total when nothing is pressed
|
|
1125
|
+
* and a point's value when something is.
|
|
1090
1126
|
*/
|
|
1091
|
-
function
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
for (let i = 0; i < n - 1; i += 1) {
|
|
1112
|
-
const slope = slopes[i]!;
|
|
1113
|
-
if (slope === 0) {
|
|
1114
|
-
result[i] = 0;
|
|
1115
|
-
result[i + 1] = 0;
|
|
1116
|
-
continue;
|
|
1117
|
-
}
|
|
1118
|
-
const a = result[i]! / slope;
|
|
1119
|
-
const b = result[i + 1]! / slope;
|
|
1120
|
-
const magnitude = Math.sqrt(a * a + b * b);
|
|
1121
|
-
if (magnitude > 3) {
|
|
1122
|
-
result[i] = (3 / magnitude) * a * slope;
|
|
1123
|
-
result[i + 1] = (3 / magnitude) * b * slope;
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
return result;
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
/** Points, split at the gaps — a null breaks the series rather than crossing it. */
|
|
1131
|
-
function runsOf(
|
|
1132
|
-
values: (number | null)[],
|
|
1133
|
-
plot: Plot,
|
|
1134
|
-
min: number,
|
|
1135
|
-
max: number
|
|
1136
|
-
): { x: number; y: number }[][] {
|
|
1137
|
-
'worklet';
|
|
1138
|
-
const runs: { x: number; y: number }[][] = [];
|
|
1139
|
-
let run: { x: number; y: number }[] = [];
|
|
1140
|
-
|
|
1141
|
-
for (let i = 0; i < values.length; i += 1) {
|
|
1142
|
-
const value = values[i];
|
|
1143
|
-
if (value === null || value === undefined) {
|
|
1144
|
-
if (run.length) runs.push(run);
|
|
1145
|
-
run = [];
|
|
1146
|
-
continue;
|
|
1147
|
-
}
|
|
1148
|
-
run.push({ x: xOf(i, values.length, plot), y: yOf(value, plot, min, max) });
|
|
1149
|
-
}
|
|
1150
|
-
if (run.length) runs.push(run);
|
|
1151
|
-
return runs;
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
function segment(points: { x: number; y: number }[], curve: LineChartCurve): string {
|
|
1155
|
-
'worklet';
|
|
1156
|
-
if (!points.length) return '';
|
|
1157
|
-
if (points.length < 3 || curve === 'linear') {
|
|
1158
|
-
return points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`).join(' ');
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
const xs = points.map((p) => p.x);
|
|
1162
|
-
const ys = points.map((p) => p.y);
|
|
1163
|
-
const ms = tangents(xs, ys);
|
|
1127
|
+
function LineChartHeader({
|
|
1128
|
+
className,
|
|
1129
|
+
title,
|
|
1130
|
+
value,
|
|
1131
|
+
caption,
|
|
1132
|
+
labels,
|
|
1133
|
+
legend = false,
|
|
1134
|
+
children,
|
|
1135
|
+
...props
|
|
1136
|
+
}: LineChartHeaderProps) {
|
|
1137
|
+
const { series } = useChart('LineChart.Header');
|
|
1138
|
+
const trailing =
|
|
1139
|
+
children ??
|
|
1140
|
+
(legend && series.length ? (
|
|
1141
|
+
<View className="flex-row flex-wrap items-center justify-end gap-x-3 gap-y-1">
|
|
1142
|
+
{series.map(([key, color]) => (
|
|
1143
|
+
<SeriesSwatch key={key} color={color} label={labels?.[key] ?? key} />
|
|
1144
|
+
))}
|
|
1145
|
+
</View>
|
|
1146
|
+
) : null);
|
|
1164
1147
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1148
|
+
return (
|
|
1149
|
+
<View
|
|
1150
|
+
{...props}
|
|
1151
|
+
className={cn('flex-row items-start justify-between gap-3 pb-3', className)}
|
|
1152
|
+
>
|
|
1153
|
+
<View className="flex-1 gap-0.5">
|
|
1154
|
+
{title ? (
|
|
1155
|
+
<Text size="xs" muted>
|
|
1156
|
+
{title}
|
|
1157
|
+
</Text>
|
|
1158
|
+
) : null}
|
|
1159
|
+
{value ? (
|
|
1160
|
+
<Text size="xl" weight="bold">
|
|
1161
|
+
{value}
|
|
1162
|
+
</Text>
|
|
1163
|
+
) : null}
|
|
1164
|
+
{caption ? (
|
|
1165
|
+
<Text size="xs" muted>
|
|
1166
|
+
{caption}
|
|
1167
|
+
</Text>
|
|
1168
|
+
) : null}
|
|
1169
|
+
</View>
|
|
1170
|
+
{/* Shrinkable, unlike a view's default in React Native. Held rigid, a
|
|
1171
|
+
three-series key takes the width it wants and the caption underneath
|
|
1172
|
+
the value wraps to two lines to make room for it. */}
|
|
1173
|
+
{trailing ? <View className="shrink pt-1">{trailing}</View> : null}
|
|
1174
|
+
</View>
|
|
1175
|
+
);
|
|
1171
1176
|
}
|
|
1177
|
+
LineChartHeader.displayName = 'LineChart.Header';
|
|
1178
|
+
LineChartHeader.layer = 'header' as Layer;
|
|
1172
1179
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
min: number,
|
|
1177
|
-
max: number,
|
|
1178
|
-
curve: LineChartCurve,
|
|
1179
|
-
loading: boolean
|
|
1180
|
-
): string {
|
|
1181
|
-
'worklet';
|
|
1182
|
-
if (loading || plot.width <= 0) {
|
|
1183
|
-
// Flat down the middle: the shape the skeleton holds, and the shape the
|
|
1184
|
-
// real series grows out of once the data arrives.
|
|
1185
|
-
const y = plot.top + plot.height / 2;
|
|
1186
|
-
return `M${plot.left},${y} L${plot.left + plot.width},${y}`;
|
|
1187
|
-
}
|
|
1188
|
-
return runsOf(values, plot, min, max)
|
|
1189
|
-
.map((run) => segment(run, curve))
|
|
1190
|
-
.join(' ');
|
|
1191
|
-
}
|
|
1180
|
+
/* -------------------------------------------------------------------------- */
|
|
1181
|
+
/* Scales and paths */
|
|
1182
|
+
/* -------------------------------------------------------------------------- */
|
|
1192
1183
|
|
|
1193
|
-
|
|
1194
|
-
values: (number | null)[],
|
|
1195
|
-
plot: Plot,
|
|
1196
|
-
min: number,
|
|
1197
|
-
max: number,
|
|
1198
|
-
curve: LineChartCurve,
|
|
1199
|
-
loading: boolean
|
|
1200
|
-
): string {
|
|
1201
|
-
'worklet';
|
|
1202
|
-
if (loading || plot.width <= 0) return '';
|
|
1203
|
-
|
|
1204
|
-
const baseline = plot.top + plot.height;
|
|
1205
|
-
return runsOf(values, plot, min, max)
|
|
1206
|
-
.map((run) => {
|
|
1207
|
-
const top = segment(run, curve);
|
|
1208
|
-
if (!top) return '';
|
|
1209
|
-
const first = run[0]!;
|
|
1210
|
-
const last = run[run.length - 1]!;
|
|
1211
|
-
return `${top} L${last.x},${baseline} L${first.x},${baseline} Z`;
|
|
1212
|
-
})
|
|
1213
|
-
.join(' ');
|
|
1214
|
-
}
|
|
1184
|
+
/** Only reached if the theme CSS was never imported. */
|
|
1215
1185
|
|
|
1216
1186
|
export const LineChart = Object.assign(LineChartRoot, {
|
|
1187
|
+
Header: LineChartHeader,
|
|
1217
1188
|
Grid: LineChartGrid,
|
|
1218
1189
|
Area: LineChartArea,
|
|
1219
1190
|
Line: LineChartLine,
|