panelui-native 0.25.0 → 0.30.0
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 +18 -1
- 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 +182 -50
- package/lib/module/components/area-chart/index.js.map +1 -1
- 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 +3 -3
- 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 +197 -56
- package/lib/module/components/bar-chart/index.js.map +1 -1
- package/lib/module/components/bottom-sheet/index.js +15 -10
- package/lib/module/components/bottom-sheet/index.js.map +1 -1
- package/lib/module/components/breadcrumb/index.js +6 -6
- package/lib/module/components/breadcrumb/index.js.map +1 -1
- 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 +792 -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 +4 -4
- package/lib/module/components/chip/index.js.map +1 -1
- package/lib/module/components/code-block/highlight.js +363 -0
- package/lib/module/components/code-block/highlight.js.map +1 -0
- package/lib/module/components/code-block/index.js +313 -0
- package/lib/module/components/code-block/index.js.map +1 -0
- package/lib/module/components/date-picker/index.js +165 -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 +2 -1
- 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 +5 -5
- package/lib/module/components/frame/index.js.map +1 -1
- package/lib/module/components/input-group/index.js +3 -2
- 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 +167 -44
- package/lib/module/components/line-chart/index.js.map +1 -1
- package/lib/module/components/map/index.js +7 -7
- package/lib/module/components/map/index.js.map +1 -1
- package/lib/module/components/marker/index.js +3 -3
- package/lib/module/components/marker/index.js.map +1 -1
- package/lib/module/components/menu/index.js +714 -0
- package/lib/module/components/menu/index.js.map +1 -0
- package/lib/module/components/message/index.js +7 -7
- 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/plan/index.js +296 -0
- package/lib/module/components/plan/index.js.map +1 -0
- package/lib/module/components/popover/index.js +73 -10
- package/lib/module/components/popover/index.js.map +1 -1
- package/lib/module/components/radio-group/index.js +15 -10
- package/lib/module/components/radio-group/index.js.map +1 -1
- package/lib/module/components/reasoning/index.js +244 -0
- package/lib/module/components/reasoning/index.js.map +1 -0
- package/lib/module/components/section-rail/index.js +4 -4
- 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/sources/index.js +221 -0
- package/lib/module/components/sources/index.js.map +1 -0
- package/lib/module/components/steps/index.js +4 -4
- package/lib/module/components/steps/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/task/index.js +300 -0
- package/lib/module/components/task/index.js.map +1 -0
- package/lib/module/components/textarea/index.js +295 -0
- package/lib/module/components/textarea/index.js.map +1 -0
- 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 +648 -0
- package/lib/module/components/tooltip/index.js.map +1 -0
- package/lib/module/components/typography/index.js +2 -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/icons/index.js +202 -0
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +13 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/primitives/collapse.js +112 -0
- package/lib/module/primitives/collapse.js.map +1 -0
- 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 +25 -1
- package/lib/module/primitives/text.js.map +1 -1
- 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 +41 -1
- package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/attachment/index.d.ts.map +1 -1
- package/lib/typescript/src/components/bar-chart/index.d.ts +44 -2
- package/lib/typescript/src/components/bar-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
- package/lib/typescript/src/components/breadcrumb/index.d.ts.map +1 -1
- package/lib/typescript/src/components/button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/calendar/index.d.ts +173 -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/code-block/highlight.d.ts +39 -0
- package/lib/typescript/src/components/code-block/highlight.d.ts.map +1 -0
- package/lib/typescript/src/components/code-block/index.d.ts +113 -0
- package/lib/typescript/src/components/code-block/index.d.ts.map +1 -0
- package/lib/typescript/src/components/date-picker/index.d.ts +97 -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.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.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 +41 -2
- package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/map/index.d.ts.map +1 -1
- package/lib/typescript/src/components/marker/index.d.ts.map +1 -1
- package/lib/typescript/src/components/menu/index.d.ts +261 -0
- package/lib/typescript/src/components/menu/index.d.ts.map +1 -0
- 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/plan/index.d.ts +127 -0
- package/lib/typescript/src/components/plan/index.d.ts.map +1 -0
- package/lib/typescript/src/components/popover/index.d.ts +35 -1
- package/lib/typescript/src/components/popover/index.d.ts.map +1 -1
- package/lib/typescript/src/components/radio-group/index.d.ts.map +1 -1
- package/lib/typescript/src/components/reasoning/index.d.ts +97 -0
- package/lib/typescript/src/components/reasoning/index.d.ts.map +1 -0
- package/lib/typescript/src/components/select/index.d.ts.map +1 -1
- package/lib/typescript/src/components/sources/index.d.ts +85 -0
- package/lib/typescript/src/components/sources/index.d.ts.map +1 -0
- package/lib/typescript/src/components/steps/index.d.ts.map +1 -1
- package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
- package/lib/typescript/src/components/task/index.d.ts +103 -0
- package/lib/typescript/src/components/task/index.d.ts.map +1 -0
- package/lib/typescript/src/components/textarea/index.d.ts +175 -0
- package/lib/typescript/src/components/textarea/index.d.ts.map +1 -0
- 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 +180 -0
- package/lib/typescript/src/components/tooltip/index.d.ts.map +1 -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/icons/index.d.ts +17 -0
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +16 -4
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/primitives/collapse.d.ts +42 -0
- package/lib/typescript/src/primitives/collapse.d.ts.map +1 -0
- 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/date.d.ts +116 -0
- package/lib/typescript/src/utils/date.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/accordion/index.tsx +8 -12
- package/src/components/alert/index.tsx +2 -2
- package/src/components/area-chart/index.tsx +208 -55
- package/src/components/attachment/index.tsx +2 -2
- package/src/components/avatar/index.tsx +3 -3
- package/src/components/badge/index.tsx +6 -6
- package/src/components/bar-chart/index.tsx +214 -54
- package/src/components/bottom-sheet/index.tsx +17 -16
- package/src/components/breadcrumb/index.tsx +6 -8
- package/src/components/button/index.tsx +12 -8
- package/src/components/calendar/index.tsx +970 -0
- package/src/components/carousel/index.tsx +788 -0
- package/src/components/chip/index.tsx +4 -6
- package/src/components/code-block/highlight.ts +278 -0
- package/src/components/code-block/index.tsx +341 -0
- package/src/components/date-picker/index.tsx +255 -0
- package/src/components/dialog/index.tsx +7 -2
- package/src/components/direction/index.tsx +2 -1
- 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 +5 -7
- package/src/components/input-group/index.tsx +3 -2
- package/src/components/item/index.tsx +9 -9
- package/src/components/label/index.tsx +2 -2
- package/src/components/line-chart/index.tsx +189 -50
- package/src/components/map/index.tsx +7 -9
- package/src/components/marker/index.tsx +3 -3
- package/src/components/menu/index.tsx +812 -0
- package/src/components/message/index.tsx +7 -7
- package/src/components/message-scroller/index.tsx +5 -4
- package/src/components/plan/index.tsx +325 -0
- package/src/components/popover/index.tsx +111 -8
- package/src/components/radio-group/index.tsx +13 -4
- package/src/components/reasoning/index.tsx +297 -0
- package/src/components/section-rail/index.tsx +4 -4
- package/src/components/select/index.tsx +10 -4
- package/src/components/sources/index.tsx +278 -0
- package/src/components/steps/index.tsx +4 -4
- package/src/components/tabs/index.tsx +7 -9
- package/src/components/task/index.tsx +329 -0
- package/src/components/textarea/index.tsx +381 -0
- 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 +793 -0
- package/src/components/typography/index.tsx +2 -2
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-back-handler.ts +37 -0
- package/src/icons/index.tsx +147 -0
- package/src/index.ts +116 -0
- package/src/primitives/collapse.tsx +128 -0
- package/src/primitives/keyboard-avoider.tsx +2 -1
- package/src/primitives/text.tsx +28 -1
- package/src/utils/date.ts +446 -0
- package/theme.css +112 -0
|
@@ -34,7 +34,7 @@ import { tv, type VariantProps } from 'tailwind-variants';
|
|
|
34
34
|
import { useCSSVariable } from 'uniwind';
|
|
35
35
|
import { IconColorProvider } from '../../icons';
|
|
36
36
|
import { AnimatedPressable } from '../../primitives/animated-pressable';
|
|
37
|
-
import { Text, type TextProps } from '../../primitives/text';
|
|
37
|
+
import { Text, type TextProps, textChildren } from '../../primitives/text';
|
|
38
38
|
import { cn } from '../../utils/cn';
|
|
39
39
|
|
|
40
40
|
const messageVariants = tv({
|
|
@@ -137,11 +137,11 @@ const MessageRoot = forwardRef<View, MessageProps>(
|
|
|
137
137
|
className={root({ className })}
|
|
138
138
|
{...props}
|
|
139
139
|
>
|
|
140
|
-
{children}
|
|
140
|
+
{textChildren(children)}
|
|
141
141
|
</AnimatedPressable>
|
|
142
142
|
) : (
|
|
143
143
|
<View ref={ref} className={root({ className })} {...props}>
|
|
144
|
-
{children}
|
|
144
|
+
{textChildren(children)}
|
|
145
145
|
</View>
|
|
146
146
|
);
|
|
147
147
|
|
|
@@ -207,7 +207,7 @@ const MessageAvatar = forwardRef<View, MessageAvatarProps>(
|
|
|
207
207
|
|
|
208
208
|
return (
|
|
209
209
|
<View ref={ref} className={avatar({ className })} {...props}>
|
|
210
|
-
{stacked ? <View className="opacity-0">{children}</View> : children}
|
|
210
|
+
{stacked ? <View className="opacity-0">{textChildren(children)}</View> : children}
|
|
211
211
|
</View>
|
|
212
212
|
);
|
|
213
213
|
}
|
|
@@ -227,7 +227,7 @@ const MessageContent = forwardRef<View, MessageContentProps>(
|
|
|
227
227
|
|
|
228
228
|
return (
|
|
229
229
|
<View ref={ref} className={content({ className })} {...props}>
|
|
230
|
-
{children}
|
|
230
|
+
{textChildren(children)}
|
|
231
231
|
</View>
|
|
232
232
|
);
|
|
233
233
|
}
|
|
@@ -275,7 +275,7 @@ const MessageBubble = forwardRef<View, MessageBubbleProps>(
|
|
|
275
275
|
return (
|
|
276
276
|
<View ref={ref} className={bubble({ className })} {...props}>
|
|
277
277
|
<IconColorProvider color={typeof foreground === 'string' ? foreground : undefined}>
|
|
278
|
-
{children}
|
|
278
|
+
{textChildren(children)}
|
|
279
279
|
</IconColorProvider>
|
|
280
280
|
</View>
|
|
281
281
|
);
|
|
@@ -334,7 +334,7 @@ const MessageActions = forwardRef<View, MessageActionsProps>(
|
|
|
334
334
|
)}
|
|
335
335
|
{...props}
|
|
336
336
|
>
|
|
337
|
-
{children}
|
|
337
|
+
{textChildren(children)}
|
|
338
338
|
</View>
|
|
339
339
|
);
|
|
340
340
|
}
|
|
@@ -56,6 +56,7 @@ import { useCSSVariable } from 'uniwind';
|
|
|
56
56
|
import { ChevronDownIcon } from '../../icons';
|
|
57
57
|
import { AnimatedPressable } from '../../primitives/animated-pressable';
|
|
58
58
|
import { cn } from '../../utils/cn';
|
|
59
|
+
import { textChildren } from '../../primitives/text';
|
|
59
60
|
|
|
60
61
|
/** Within this many pixels of the bottom still counts as being at the bottom. */
|
|
61
62
|
const END_THRESHOLD = 24;
|
|
@@ -233,7 +234,7 @@ function MessageScrollerRoot({
|
|
|
233
234
|
return (
|
|
234
235
|
<MessageScrollerContext.Provider value={context}>
|
|
235
236
|
<View className={cn('relative overflow-hidden', className)} {...props}>
|
|
236
|
-
{children}
|
|
237
|
+
{textChildren(children)}
|
|
237
238
|
</View>
|
|
238
239
|
</MessageScrollerContext.Provider>
|
|
239
240
|
);
|
|
@@ -404,7 +405,7 @@ function MessageScrollerViewport({
|
|
|
404
405
|
onMomentumScrollEnd={settleAnchor}
|
|
405
406
|
onContentSizeChange={onContentSizeChange}
|
|
406
407
|
>
|
|
407
|
-
{children}
|
|
408
|
+
{textChildren(children)}
|
|
408
409
|
</Animated.ScrollView>
|
|
409
410
|
);
|
|
410
411
|
}
|
|
@@ -445,7 +446,7 @@ function MessageScrollerContent({
|
|
|
445
446
|
className={cn('gap-3 p-4 pb-16', className)}
|
|
446
447
|
{...props}
|
|
447
448
|
>
|
|
448
|
-
{children}
|
|
449
|
+
{textChildren(children)}
|
|
449
450
|
</View>
|
|
450
451
|
);
|
|
451
452
|
}
|
|
@@ -487,7 +488,7 @@ function MessageScrollerItem({
|
|
|
487
488
|
|
|
488
489
|
return (
|
|
489
490
|
<View className={className} {...props} onLayout={onLayout}>
|
|
490
|
-
{children}
|
|
491
|
+
{textChildren(children)}
|
|
491
492
|
</View>
|
|
492
493
|
);
|
|
493
494
|
}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan — what an agent intends to do, before it does it.
|
|
3
|
+
*
|
|
4
|
+
* A card rather than a run of rows, because a plan is a thing the reader is
|
|
5
|
+
* being asked to approve: it needs an edge around it so that where it starts
|
|
6
|
+
* and stops is not a matter of interpretation, and a footer that can hold the
|
|
7
|
+
* button that approves it.
|
|
8
|
+
*
|
|
9
|
+
* It streams. `isStreaming` puts a shimmer on the title and the description,
|
|
10
|
+
* which is the honest way to render a heading that is still arriving — the
|
|
11
|
+
* alternative is a title that grows a word at a time and reads as finished at
|
|
12
|
+
* every intermediate length.
|
|
13
|
+
*
|
|
14
|
+
* ```tsx
|
|
15
|
+
* <Plan isStreaming={streaming}>
|
|
16
|
+
* <Plan.Header>
|
|
17
|
+
* <Plan.Title>Migrate the calendar</Plan.Title>
|
|
18
|
+
* <Plan.Description>Four files, no API change.</Plan.Description>
|
|
19
|
+
* <Plan.Action><Plan.Trigger /></Plan.Action>
|
|
20
|
+
* </Plan.Header>
|
|
21
|
+
* <Plan.Content>…</Plan.Content>
|
|
22
|
+
* <Plan.Footer><Button>Approve</Button></Plan.Footer>
|
|
23
|
+
* </Plan>
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* ## Where the props come from
|
|
27
|
+
*
|
|
28
|
+
* With the AI SDK a plan is usually an `experimental_useObject` stream, where
|
|
29
|
+
* `isStreaming` is the hook's `isLoading` and the fields arrive one at a time —
|
|
30
|
+
* which is exactly the case the shimmer exists for, since a partial object has
|
|
31
|
+
* a title before it has anything else.
|
|
32
|
+
*/
|
|
33
|
+
import {
|
|
34
|
+
createContext,
|
|
35
|
+
useCallback,
|
|
36
|
+
useContext,
|
|
37
|
+
useEffect,
|
|
38
|
+
useMemo,
|
|
39
|
+
useState,
|
|
40
|
+
type ReactNode,
|
|
41
|
+
} from 'react';
|
|
42
|
+
import { Pressable, View, type PressableProps, type ViewProps } from 'react-native';
|
|
43
|
+
import Animated, {
|
|
44
|
+
useAnimatedStyle,
|
|
45
|
+
useReducedMotion,
|
|
46
|
+
useSharedValue,
|
|
47
|
+
withTiming,
|
|
48
|
+
} from 'react-native-reanimated';
|
|
49
|
+
import { tv } from 'tailwind-variants';
|
|
50
|
+
import { ChevronDownIcon } from '../../icons';
|
|
51
|
+
import { Collapse } from '../../primitives/collapse';
|
|
52
|
+
import { Text, textChildren } from '../../primitives/text';
|
|
53
|
+
import { cn } from '../../utils/cn';
|
|
54
|
+
import { Shimmer } from '../shimmer';
|
|
55
|
+
|
|
56
|
+
const planVariants = tv({
|
|
57
|
+
slots: {
|
|
58
|
+
root: 'w-full overflow-hidden rounded-2xl border border-border bg-card',
|
|
59
|
+
header: 'flex-row items-start gap-3 p-4',
|
|
60
|
+
heading: 'min-w-0 flex-1 gap-1',
|
|
61
|
+
title: 'text-base font-semibold text-card-foreground',
|
|
62
|
+
description: 'text-sm text-muted-foreground',
|
|
63
|
+
action: 'shrink-0 flex-row items-center gap-1',
|
|
64
|
+
trigger: 'h-8 w-8 items-center justify-center rounded-full active:bg-accent',
|
|
65
|
+
content: 'gap-2 px-4 pb-4',
|
|
66
|
+
footer: 'flex-row items-center justify-end gap-2 border-t border-border p-4',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
interface PlanContextValue {
|
|
71
|
+
isStreaming: boolean;
|
|
72
|
+
open: boolean;
|
|
73
|
+
setOpen: (open: boolean) => void;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const PlanContext = createContext<PlanContextValue | null>(null);
|
|
77
|
+
|
|
78
|
+
function usePlan(component: string): PlanContextValue {
|
|
79
|
+
const context = useContext(PlanContext);
|
|
80
|
+
if (!context) {
|
|
81
|
+
throw new Error(`${component} must be used within a <Plan>`);
|
|
82
|
+
}
|
|
83
|
+
return context;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface PlanProps extends Omit<ViewProps, 'children'> {
|
|
87
|
+
className?: string;
|
|
88
|
+
/** Whether the plan is still being written. Shimmers the title and description. */
|
|
89
|
+
isStreaming?: boolean;
|
|
90
|
+
/** Controlled open state of the body. */
|
|
91
|
+
open?: boolean;
|
|
92
|
+
/** Initial state when uncontrolled. */
|
|
93
|
+
defaultOpen?: boolean;
|
|
94
|
+
onOpenChange?: (open: boolean) => void;
|
|
95
|
+
children?: ReactNode;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function PlanRoot({
|
|
99
|
+
className,
|
|
100
|
+
isStreaming = false,
|
|
101
|
+
open: openProp,
|
|
102
|
+
defaultOpen = true,
|
|
103
|
+
onOpenChange,
|
|
104
|
+
children,
|
|
105
|
+
...props
|
|
106
|
+
}: PlanProps) {
|
|
107
|
+
const { root } = planVariants();
|
|
108
|
+
const [internalOpen, setInternalOpen] = useState(defaultOpen);
|
|
109
|
+
const isControlled = openProp !== undefined;
|
|
110
|
+
const open = isControlled ? openProp : internalOpen;
|
|
111
|
+
|
|
112
|
+
const setOpen = useCallback(
|
|
113
|
+
(next: boolean) => {
|
|
114
|
+
if (!isControlled) setInternalOpen(next);
|
|
115
|
+
onOpenChange?.(next);
|
|
116
|
+
},
|
|
117
|
+
[isControlled, onOpenChange]
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const context = useMemo(
|
|
121
|
+
() => ({ isStreaming, open, setOpen }),
|
|
122
|
+
[isStreaming, open, setOpen]
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<PlanContext.Provider value={context}>
|
|
127
|
+
<View {...props} className={cn(root(), className)}>
|
|
128
|
+
{children}
|
|
129
|
+
</View>
|
|
130
|
+
</PlanContext.Provider>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
PlanRoot.displayName = 'Plan';
|
|
134
|
+
|
|
135
|
+
export interface PlanHeaderProps extends ViewProps {
|
|
136
|
+
className?: string;
|
|
137
|
+
children?: ReactNode;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The title, the description and whatever acts on them.
|
|
142
|
+
*
|
|
143
|
+
* The heading is wrapped in a column of its own so the action stays pinned to
|
|
144
|
+
* the trailing edge as the title wraps, rather than riding down with it.
|
|
145
|
+
*/
|
|
146
|
+
function PlanHeader({ className, children, ...props }: PlanHeaderProps) {
|
|
147
|
+
const { header, heading } = planVariants();
|
|
148
|
+
|
|
149
|
+
const headings: ReactNode[] = [];
|
|
150
|
+
const actions: ReactNode[] = [];
|
|
151
|
+
for (const child of Array.isArray(children) ? children : [children]) {
|
|
152
|
+
if (isPlanAction(child)) actions.push(child);
|
|
153
|
+
else headings.push(child);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<View {...props} className={cn(header(), className)}>
|
|
158
|
+
<View className={heading()}>{headings}</View>
|
|
159
|
+
{actions}
|
|
160
|
+
</View>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function isPlanAction(child: ReactNode): boolean {
|
|
165
|
+
return (
|
|
166
|
+
typeof child === 'object' &&
|
|
167
|
+
child !== null &&
|
|
168
|
+
'type' in child &&
|
|
169
|
+
(child as { type?: unknown }).type === PlanAction
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface PlanTitleProps extends ViewProps {
|
|
174
|
+
className?: string;
|
|
175
|
+
children?: ReactNode;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** The plan's name. Shimmers while it is still being written. */
|
|
179
|
+
function PlanTitle({ className, children, ...props }: PlanTitleProps) {
|
|
180
|
+
const { isStreaming } = usePlan('Plan.Title');
|
|
181
|
+
const { title } = planVariants();
|
|
182
|
+
|
|
183
|
+
if (isStreaming && typeof children === 'string') {
|
|
184
|
+
return <Shimmer textClassName={cn(title(), className)}>{children}</Shimmer>;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<Text accessibilityRole="header" className={cn(title(), className)} {...props}>
|
|
189
|
+
{children}
|
|
190
|
+
</Text>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export interface PlanDescriptionProps extends ViewProps {
|
|
195
|
+
className?: string;
|
|
196
|
+
children?: ReactNode;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function PlanDescription({ className, children, ...props }: PlanDescriptionProps) {
|
|
200
|
+
const { isStreaming } = usePlan('Plan.Description');
|
|
201
|
+
const { description } = planVariants();
|
|
202
|
+
|
|
203
|
+
if (isStreaming && typeof children === 'string') {
|
|
204
|
+
return <Shimmer textClassName={cn(description(), className)}>{children}</Shimmer>;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return (
|
|
208
|
+
<Text className={cn(description(), className)} {...props}>
|
|
209
|
+
{children}
|
|
210
|
+
</Text>
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export interface PlanActionProps extends ViewProps {
|
|
215
|
+
className?: string;
|
|
216
|
+
children?: ReactNode;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Pinned to the header's trailing edge — the toggle, a badge, a menu. */
|
|
220
|
+
function PlanAction({ className, children, ...props }: PlanActionProps) {
|
|
221
|
+
const { action } = planVariants();
|
|
222
|
+
return (
|
|
223
|
+
<View {...props} className={cn(action(), className)}>
|
|
224
|
+
{children}
|
|
225
|
+
</View>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export interface PlanTriggerProps extends Omit<PressableProps, 'children' | 'style'> {
|
|
230
|
+
className?: string;
|
|
231
|
+
children?: ReactNode;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Folds the body away. Its chevron turns to point at the state it will reach. */
|
|
235
|
+
function PlanTrigger({ className, children, onPress, ...props }: PlanTriggerProps) {
|
|
236
|
+
const { open, setOpen } = usePlan('Plan.Trigger');
|
|
237
|
+
const { trigger } = planVariants();
|
|
238
|
+
const reducedMotion = useReducedMotion();
|
|
239
|
+
const progress = useSharedValue(open ? 1 : 0);
|
|
240
|
+
|
|
241
|
+
useEffect(() => {
|
|
242
|
+
progress.value = reducedMotion
|
|
243
|
+
? open
|
|
244
|
+
? 1
|
|
245
|
+
: 0
|
|
246
|
+
: withTiming(open ? 1 : 0, { duration: 180 });
|
|
247
|
+
}, [open, reducedMotion, progress]);
|
|
248
|
+
|
|
249
|
+
const chevronStyle = useAnimatedStyle(() => ({
|
|
250
|
+
transform: [{ rotate: `${progress.value * 180}deg` }],
|
|
251
|
+
}));
|
|
252
|
+
|
|
253
|
+
return (
|
|
254
|
+
<Pressable
|
|
255
|
+
accessibilityRole="button"
|
|
256
|
+
accessibilityLabel={open ? 'Collapse plan' : 'Expand plan'}
|
|
257
|
+
accessibilityState={{ expanded: open }}
|
|
258
|
+
hitSlop={6}
|
|
259
|
+
onPress={(event) => {
|
|
260
|
+
onPress?.(event);
|
|
261
|
+
setOpen(!open);
|
|
262
|
+
}}
|
|
263
|
+
className={cn(trigger(), className)}
|
|
264
|
+
{...props}
|
|
265
|
+
>
|
|
266
|
+
{children ?? (
|
|
267
|
+
<Animated.View style={chevronStyle}>
|
|
268
|
+
<ChevronDownIcon size={16} />
|
|
269
|
+
</Animated.View>
|
|
270
|
+
)}
|
|
271
|
+
</Pressable>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export interface PlanContentProps extends Omit<ViewProps, 'children'> {
|
|
276
|
+
className?: string;
|
|
277
|
+
children?: ReactNode;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** The steps. Collapses rather than unmounting, so it can still be growing. */
|
|
281
|
+
function PlanContent({ className, children, ...props }: PlanContentProps) {
|
|
282
|
+
const { open } = usePlan('Plan.Content');
|
|
283
|
+
const { content, description } = planVariants();
|
|
284
|
+
|
|
285
|
+
return (
|
|
286
|
+
<Collapse open={open} className={cn(content(), className)} {...props}>
|
|
287
|
+
{textChildren(children, (text) => (
|
|
288
|
+
<Text className={description()}>{text}</Text>
|
|
289
|
+
))}
|
|
290
|
+
</Collapse>
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export interface PlanFooterProps extends ViewProps {
|
|
295
|
+
className?: string;
|
|
296
|
+
children?: ReactNode;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** Where the button that approves the plan goes. */
|
|
300
|
+
function PlanFooter({ className, children, ...props }: PlanFooterProps) {
|
|
301
|
+
const { footer } = planVariants();
|
|
302
|
+
return (
|
|
303
|
+
<View {...props} className={cn(footer(), className)}>
|
|
304
|
+
{children}
|
|
305
|
+
</View>
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
PlanHeader.displayName = 'Plan.Header';
|
|
310
|
+
PlanTitle.displayName = 'Plan.Title';
|
|
311
|
+
PlanDescription.displayName = 'Plan.Description';
|
|
312
|
+
PlanAction.displayName = 'Plan.Action';
|
|
313
|
+
PlanTrigger.displayName = 'Plan.Trigger';
|
|
314
|
+
PlanContent.displayName = 'Plan.Content';
|
|
315
|
+
PlanFooter.displayName = 'Plan.Footer';
|
|
316
|
+
|
|
317
|
+
export const Plan = Object.assign(PlanRoot, {
|
|
318
|
+
Header: PlanHeader,
|
|
319
|
+
Title: PlanTitle,
|
|
320
|
+
Description: PlanDescription,
|
|
321
|
+
Action: PlanAction,
|
|
322
|
+
Trigger: PlanTrigger,
|
|
323
|
+
Content: PlanContent,
|
|
324
|
+
Footer: PlanFooter,
|
|
325
|
+
});
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
} from 'react';
|
|
45
45
|
import {
|
|
46
46
|
Pressable,
|
|
47
|
+
ScrollView,
|
|
47
48
|
useWindowDimensions,
|
|
48
49
|
View,
|
|
49
50
|
type LayoutChangeEvent,
|
|
@@ -60,8 +61,9 @@ import Animated, {
|
|
|
60
61
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
61
62
|
import { Portal } from '../../primitives/portal';
|
|
62
63
|
import { Scrim } from '../../primitives/scrim';
|
|
64
|
+
import { useBackHandler } from '../../hooks/use-back-handler';
|
|
63
65
|
import { BottomSheet } from '../bottom-sheet';
|
|
64
|
-
import { Text, type TextProps } from '../../primitives/text';
|
|
66
|
+
import { Text, type TextProps, textChildren } from '../../primitives/text';
|
|
65
67
|
import { cn } from '../../utils/cn';
|
|
66
68
|
|
|
67
69
|
/** Gap between the trigger and the panel. */
|
|
@@ -227,6 +229,40 @@ export interface PopoverContentProps extends ViewProps {
|
|
|
227
229
|
* content into a column.
|
|
228
230
|
*/
|
|
229
231
|
minWidth?: number;
|
|
232
|
+
/**
|
|
233
|
+
* Ceiling for the panel's height, in pixels. Always clamped to the room
|
|
234
|
+
* inside the safe area, which is also the default — a panel is never
|
|
235
|
+
* positioned so that part of it falls off the screen, because the part that
|
|
236
|
+
* falls off cannot be scrolled back into view.
|
|
237
|
+
*/
|
|
238
|
+
maxHeight?: number;
|
|
239
|
+
/**
|
|
240
|
+
* Scroll the panel's body when it is taller than `maxHeight`.
|
|
241
|
+
*
|
|
242
|
+
* Off by default, because a popover is usually a paragraph or a short form
|
|
243
|
+
* and a scroller around either one only adds a bounce. Worth turning on for
|
|
244
|
+
* a list of unknown length, which is the case where the cap actually bites.
|
|
245
|
+
*
|
|
246
|
+
* The spacing between children moves to the scroller's content when this is
|
|
247
|
+
* set; `className` still dresses the panel itself.
|
|
248
|
+
*/
|
|
249
|
+
scrollable?: boolean;
|
|
250
|
+
/**
|
|
251
|
+
* Drop the panel's own surface — its background, border, radius, padding and
|
|
252
|
+
* shadow — and keep only its position and its size. For a caller that draws
|
|
253
|
+
* the surface itself, so that something can be put *behind* the content
|
|
254
|
+
* rather than layered on top of a background that is already painted.
|
|
255
|
+
*
|
|
256
|
+
* The panel is still clipped to a rounded rectangle, because a surface drawn
|
|
257
|
+
* inside it has to have something to be clipped by.
|
|
258
|
+
*/
|
|
259
|
+
unstyled?: boolean;
|
|
260
|
+
/**
|
|
261
|
+
* A layer drawn inside the panel, behind its content — and, crucially,
|
|
262
|
+
* outside its scroller, so that a surface does not scroll away with the rows
|
|
263
|
+
* on top of it. Pair it with `unstyled` to own the panel's appearance.
|
|
264
|
+
*/
|
|
265
|
+
background?: ReactNode;
|
|
230
266
|
/** Tap outside the panel closes it. Default true. */
|
|
231
267
|
dismissible?: boolean;
|
|
232
268
|
/**
|
|
@@ -246,6 +282,10 @@ function PopoverContent({
|
|
|
246
282
|
alignOffset = 0,
|
|
247
283
|
width = 'content-fit',
|
|
248
284
|
minWidth,
|
|
285
|
+
maxHeight,
|
|
286
|
+
scrollable = false,
|
|
287
|
+
unstyled = false,
|
|
288
|
+
background,
|
|
249
289
|
dismissible = true,
|
|
250
290
|
blur = false,
|
|
251
291
|
children,
|
|
@@ -255,6 +295,13 @@ function PopoverContent({
|
|
|
255
295
|
}: PopoverContentProps) {
|
|
256
296
|
const context = usePopover('Popover.Content');
|
|
257
297
|
const { open, setOpen, trigger, setPlacement, setArrowOffset, presentation } = context;
|
|
298
|
+
|
|
299
|
+
// The anchored panel owns the Android back button while it is up. The sheet
|
|
300
|
+
// presentation is left alone — BottomSheet installs its own handler.
|
|
301
|
+
useBackHandler(open && dismissible && presentation !== 'bottom-sheet', () =>
|
|
302
|
+
setOpen(false)
|
|
303
|
+
);
|
|
304
|
+
|
|
258
305
|
const { width: screenWidth, height: screenHeight } = useWindowDimensions();
|
|
259
306
|
const insets = useSafeAreaInsets();
|
|
260
307
|
const [size, setSize] = useState<{ width: number; height: number } | null>(null);
|
|
@@ -295,6 +342,17 @@ function PopoverContent({
|
|
|
295
342
|
? Math.min(Math.max(requestedWidth, minWidth), available)
|
|
296
343
|
: requestedWidth;
|
|
297
344
|
|
|
345
|
+
/*
|
|
346
|
+
* The cap is what keeps a tall panel reachable. `place` clamps the panel
|
|
347
|
+
* inside the bounds, but a panel taller than the bounds cannot be clamped
|
|
348
|
+
* into them — it gets pinned to the top edge and the rest runs off the
|
|
349
|
+
* bottom of the screen, where there is no way to get at it. Capping the
|
|
350
|
+
* height first means the clamp always has a solution, and `scrollable`
|
|
351
|
+
* hands the overflow back to the finger.
|
|
352
|
+
*/
|
|
353
|
+
const room = bounds.bottom - bounds.top;
|
|
354
|
+
const resolvedMaxHeight = maxHeight === undefined ? room : Math.min(maxHeight, room);
|
|
355
|
+
|
|
298
356
|
const position =
|
|
299
357
|
trigger && size
|
|
300
358
|
? place({ trigger, size, placement, align, offset, alignOffset, bounds })
|
|
@@ -349,14 +407,42 @@ function PopoverContent({
|
|
|
349
407
|
],
|
|
350
408
|
}));
|
|
351
409
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
410
|
+
/*
|
|
411
|
+
* The sheet presentation hands off entirely to BottomSheet — it owns its own
|
|
412
|
+
* portal, backdrop and dismiss gesture, so there is nothing to anchor here.
|
|
413
|
+
* The context is re-provided inside so Popover.Close keeps closing it.
|
|
414
|
+
*
|
|
415
|
+
* `width` still means something, though. A sheet is the full width of the
|
|
416
|
+
* screen and lays its children out in a column, so a panel sized to its
|
|
417
|
+
* content takes the cross-axis *start* and ends up flush against one edge —
|
|
418
|
+
* the right-hand one under RTL, directly below the close button. Centring it
|
|
419
|
+
* is what makes `width="content-fit"` mean the same thing in both
|
|
420
|
+
* presentations.
|
|
421
|
+
*
|
|
422
|
+
* The className goes on an inner view rather than on the sheet: passed to
|
|
423
|
+
* the surface it is merged into the sheet's own padding classes and replaces
|
|
424
|
+
* them, so a panel asking for `p-3` silently strips the sheet's `px-5 pt-2`.
|
|
425
|
+
*/
|
|
355
426
|
if (presentation === 'bottom-sheet') {
|
|
356
427
|
return (
|
|
357
428
|
<BottomSheet open={open} onOpenChange={setOpen}>
|
|
358
|
-
<BottomSheet.Content dismissible={dismissible}
|
|
359
|
-
<PopoverContext.Provider value={context}>
|
|
429
|
+
<BottomSheet.Content dismissible={dismissible}>
|
|
430
|
+
<PopoverContext.Provider value={context}>
|
|
431
|
+
<View
|
|
432
|
+
className={cn(width === 'content-fit' && 'self-center', className)}
|
|
433
|
+
style={[
|
|
434
|
+
width === 'full' || width === 'trigger' ? { width: '100%' } : null,
|
|
435
|
+
typeof width === 'number' ? { width } : null,
|
|
436
|
+
minWidth === undefined ? null : { minWidth },
|
|
437
|
+
// Clear of the close button, which floats over the top-end
|
|
438
|
+
// corner of the sheet rather than sitting in the flow.
|
|
439
|
+
{ paddingTop: 8 },
|
|
440
|
+
style,
|
|
441
|
+
]}
|
|
442
|
+
>
|
|
443
|
+
{textChildren(children)}
|
|
444
|
+
</View>
|
|
445
|
+
</PopoverContext.Provider>
|
|
360
446
|
</BottomSheet.Content>
|
|
361
447
|
</BottomSheet>
|
|
362
448
|
);
|
|
@@ -398,6 +484,7 @@ function PopoverContent({
|
|
|
398
484
|
top: position?.top ?? -9999,
|
|
399
485
|
left: position?.left ?? -9999,
|
|
400
486
|
maxWidth: bounds.right - bounds.left,
|
|
487
|
+
maxHeight: resolvedMaxHeight,
|
|
401
488
|
width: resolvedWidth,
|
|
402
489
|
}}
|
|
403
490
|
>
|
|
@@ -405,12 +492,28 @@ function PopoverContent({
|
|
|
405
492
|
accessibilityViewIsModal
|
|
406
493
|
style={[panelStyle, style]}
|
|
407
494
|
className={cn(
|
|
408
|
-
|
|
495
|
+
// `unstyled` keeps the clip and drops everything that paints,
|
|
496
|
+
// so a caller can put its own layer underneath the content.
|
|
497
|
+
unstyled
|
|
498
|
+
? 'overflow-hidden rounded-2xl'
|
|
499
|
+
: 'rounded-2xl border border-border bg-popover p-4 shadow-lg',
|
|
500
|
+
// The gap belongs to whichever view actually holds the
|
|
501
|
+
// children — on the panel normally, on the scroller's content
|
|
502
|
+
// when there is one, since the scroller is then the panel's
|
|
503
|
+
// only child and a gap around it spaces nothing.
|
|
504
|
+
scrollable ? 'overflow-hidden' : 'gap-1.5',
|
|
409
505
|
className
|
|
410
506
|
)}
|
|
411
507
|
{...props}
|
|
412
508
|
>
|
|
413
|
-
{
|
|
509
|
+
{background}
|
|
510
|
+
{scrollable ? (
|
|
511
|
+
<ScrollView bounces={false} showsVerticalScrollIndicator={false}>
|
|
512
|
+
<View className="gap-1.5">{textChildren(children)}</View>
|
|
513
|
+
</ScrollView>
|
|
514
|
+
) : (
|
|
515
|
+
textChildren(children)
|
|
516
|
+
)}
|
|
414
517
|
</Animated.View>
|
|
415
518
|
</Animated.View>
|
|
416
519
|
</View>
|
|
@@ -35,7 +35,7 @@ import Animated, {
|
|
|
35
35
|
} from 'react-native-reanimated';
|
|
36
36
|
import { tv, type VariantProps } from 'tailwind-variants';
|
|
37
37
|
import { cn } from '../../utils/cn';
|
|
38
|
-
import { Text } from '../../primitives/text';
|
|
38
|
+
import { Text, textChildren } from '../../primitives/text';
|
|
39
39
|
|
|
40
40
|
type RadioVariant = 'dot' | 'card';
|
|
41
41
|
type RadioOrientation = 'vertical' | 'horizontal';
|
|
@@ -152,7 +152,7 @@ const RadioGroupRoot = forwardRef<View, RadioGroupProps>(
|
|
|
152
152
|
)}
|
|
153
153
|
{...props}
|
|
154
154
|
>
|
|
155
|
-
{children}
|
|
155
|
+
{textChildren(children)}
|
|
156
156
|
</View>
|
|
157
157
|
</RadioGroupContext.Provider>
|
|
158
158
|
);
|
|
@@ -211,13 +211,22 @@ const RadioGroupItem = forwardRef<View, RadioGroupItemProps>(
|
|
|
211
211
|
</View>
|
|
212
212
|
);
|
|
213
213
|
|
|
214
|
+
// Either way the label is a string in a row that is otherwise all views,
|
|
215
|
+
// so children given instead of the prop are dressed the same way rather
|
|
216
|
+
// than reaching the pressable bare.
|
|
217
|
+
const labelled = label ? (
|
|
218
|
+
<Text className={slots.label()}>{label}</Text>
|
|
219
|
+
) : (
|
|
220
|
+
textChildren(children, (text) => <Text className={slots.label()}>{text}</Text>)
|
|
221
|
+
);
|
|
222
|
+
|
|
214
223
|
// The card lays its text out first so the disc sits at the trailing edge;
|
|
215
224
|
// the dot row keeps the classic disc-then-label order.
|
|
216
225
|
const body =
|
|
217
226
|
variant === 'card' ? (
|
|
218
227
|
<>
|
|
219
228
|
<View className="flex-1 gap-1">
|
|
220
|
-
{
|
|
229
|
+
{labelled}
|
|
221
230
|
{description ? (
|
|
222
231
|
<Text className={slots.description()}>{description}</Text>
|
|
223
232
|
) : null}
|
|
@@ -227,7 +236,7 @@ const RadioGroupItem = forwardRef<View, RadioGroupItemProps>(
|
|
|
227
236
|
) : (
|
|
228
237
|
<>
|
|
229
238
|
{indicator}
|
|
230
|
-
{
|
|
239
|
+
{labelled}
|
|
231
240
|
</>
|
|
232
241
|
);
|
|
233
242
|
|