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
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reasoning — the model's working, shown while it happens and folded away after.
|
|
3
|
+
*
|
|
4
|
+
* A reasoning trace is interesting for exactly as long as it is the only thing
|
|
5
|
+
* arriving. Once the answer starts, it is a wall of text above the thing the
|
|
6
|
+
* reader actually asked for. So it opens itself when the trace starts streaming
|
|
7
|
+
* and closes itself about a second after it stops — once, and only if it ever
|
|
8
|
+
* streamed, because a trace that arrives complete was never a live thing to
|
|
9
|
+
* watch and should not perform.
|
|
10
|
+
*
|
|
11
|
+
* The trigger changes with it: a shimmering "Thinking…" while the tokens are
|
|
12
|
+
* coming, and "Thought for 8 seconds" afterwards. The duration is measured from
|
|
13
|
+
* the first streaming frame rather than taken on trust, so it is the time the
|
|
14
|
+
* reader actually waited.
|
|
15
|
+
*
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <Reasoning isStreaming={streaming}>
|
|
18
|
+
* <Reasoning.Trigger />
|
|
19
|
+
* <Reasoning.Content>{text}</Reasoning.Content>
|
|
20
|
+
* </Reasoning>
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* ## Where the props come from
|
|
24
|
+
*
|
|
25
|
+
* With the AI SDK, `isStreaming` is whether the last part of the last message
|
|
26
|
+
* is a reasoning part while the request is still streaming, and the content is
|
|
27
|
+
* every `reasoning` part joined together. Joined, not one component each:
|
|
28
|
+
* some models emit a run of separate reasoning parts, and one component per
|
|
29
|
+
* part is a column of "Thinking…" rows for a single thought.
|
|
30
|
+
*/
|
|
31
|
+
import {
|
|
32
|
+
createContext,
|
|
33
|
+
useCallback,
|
|
34
|
+
useContext,
|
|
35
|
+
useEffect,
|
|
36
|
+
useMemo,
|
|
37
|
+
useRef,
|
|
38
|
+
useState,
|
|
39
|
+
type ReactNode,
|
|
40
|
+
} from 'react';
|
|
41
|
+
import { Pressable, View, type PressableProps, type ViewProps } from 'react-native';
|
|
42
|
+
import Animated, {
|
|
43
|
+
useAnimatedStyle,
|
|
44
|
+
useReducedMotion,
|
|
45
|
+
useSharedValue,
|
|
46
|
+
withTiming,
|
|
47
|
+
} from 'react-native-reanimated';
|
|
48
|
+
import { tv } from 'tailwind-variants';
|
|
49
|
+
import { ChevronDownIcon, SparklesIcon } from '../../icons';
|
|
50
|
+
import { Collapse } from '../../primitives/collapse';
|
|
51
|
+
import { Text, textChildren } from '../../primitives/text';
|
|
52
|
+
import { cn } from '../../utils/cn';
|
|
53
|
+
import { Shimmer } from '../shimmer';
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* How long the finished trace stays open before folding away.
|
|
57
|
+
*
|
|
58
|
+
* Long enough that the fold reads as a consequence of the trace ending rather
|
|
59
|
+
* than as part of it, short enough that nobody is waiting on it.
|
|
60
|
+
*/
|
|
61
|
+
const AUTO_CLOSE_DELAY = 1000;
|
|
62
|
+
|
|
63
|
+
const reasoningVariants = tv({
|
|
64
|
+
slots: {
|
|
65
|
+
root: 'w-full gap-2',
|
|
66
|
+
trigger: 'flex-row items-center gap-2 py-0.5',
|
|
67
|
+
label: 'text-sm text-muted-foreground',
|
|
68
|
+
content: 'gap-2 ps-6',
|
|
69
|
+
contentText: 'text-sm leading-relaxed text-muted-foreground',
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
interface ReasoningContextValue {
|
|
74
|
+
isStreaming: boolean;
|
|
75
|
+
open: boolean;
|
|
76
|
+
setOpen: (open: boolean) => void;
|
|
77
|
+
/** Seconds the trace took, once it has finished. */
|
|
78
|
+
duration: number | undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const ReasoningContext = createContext<ReasoningContextValue | null>(null);
|
|
82
|
+
|
|
83
|
+
function useReasoning(component: string): ReasoningContextValue {
|
|
84
|
+
const context = useContext(ReasoningContext);
|
|
85
|
+
if (!context) {
|
|
86
|
+
throw new Error(`${component} must be used within a <Reasoning>`);
|
|
87
|
+
}
|
|
88
|
+
return context;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface ReasoningProps extends Omit<ViewProps, 'children'> {
|
|
92
|
+
className?: string;
|
|
93
|
+
/**
|
|
94
|
+
* Whether the trace is still arriving. Drives the shimmer on the trigger,
|
|
95
|
+
* opens the panel on the way in and closes it a beat after it goes false.
|
|
96
|
+
*/
|
|
97
|
+
isStreaming?: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* How long the trace took, in seconds. Measured from the first streaming
|
|
100
|
+
* frame when it is not given, which is the number worth showing — it is what
|
|
101
|
+
* the reader actually waited.
|
|
102
|
+
*/
|
|
103
|
+
duration?: number;
|
|
104
|
+
/** Controlled open state. */
|
|
105
|
+
open?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Initial state when uncontrolled. Defaults to whether it is streaming, so a
|
|
108
|
+
* live trace is open and a finished one arrives folded. Passing `false`
|
|
109
|
+
* explicitly also opts out of the auto-open.
|
|
110
|
+
*/
|
|
111
|
+
defaultOpen?: boolean;
|
|
112
|
+
onOpenChange?: (open: boolean) => void;
|
|
113
|
+
children?: ReactNode;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function ReasoningRoot({
|
|
117
|
+
className,
|
|
118
|
+
isStreaming = false,
|
|
119
|
+
duration: durationProp,
|
|
120
|
+
open: openProp,
|
|
121
|
+
defaultOpen,
|
|
122
|
+
onOpenChange,
|
|
123
|
+
children,
|
|
124
|
+
...props
|
|
125
|
+
}: ReasoningProps) {
|
|
126
|
+
const { root } = reasoningVariants();
|
|
127
|
+
const [internalOpen, setInternalOpen] = useState(defaultOpen ?? isStreaming);
|
|
128
|
+
const [measured, setMeasured] = useState<number>();
|
|
129
|
+
|
|
130
|
+
const isControlled = openProp !== undefined;
|
|
131
|
+
const open = isControlled ? openProp : internalOpen;
|
|
132
|
+
|
|
133
|
+
const setOpen = useCallback(
|
|
134
|
+
(next: boolean) => {
|
|
135
|
+
if (!isControlled) setInternalOpen(next);
|
|
136
|
+
onOpenChange?.(next);
|
|
137
|
+
},
|
|
138
|
+
[isControlled, onOpenChange]
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
/*
|
|
142
|
+
* A caller that said `defaultOpen={false}` meant it. Without this the panel
|
|
143
|
+
* would spring open the moment the trace started, which is the one thing that
|
|
144
|
+
* request was asking not to happen.
|
|
145
|
+
*/
|
|
146
|
+
const optedOut = defaultOpen === false;
|
|
147
|
+
const everStreamed = useRef(isStreaming);
|
|
148
|
+
const startedAt = useRef<number | null>(null);
|
|
149
|
+
const [autoClosed, setAutoClosed] = useState(false);
|
|
150
|
+
|
|
151
|
+
// Timed here rather than taken on trust: `duration` is optional, and the
|
|
152
|
+
// honest number is the wall clock between the first token and the last.
|
|
153
|
+
useEffect(() => {
|
|
154
|
+
if (isStreaming) {
|
|
155
|
+
everStreamed.current = true;
|
|
156
|
+
startedAt.current ??= Date.now();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (startedAt.current !== null) {
|
|
160
|
+
setMeasured(Math.max(1, Math.round((Date.now() - startedAt.current) / 1000)));
|
|
161
|
+
startedAt.current = null;
|
|
162
|
+
}
|
|
163
|
+
}, [isStreaming]);
|
|
164
|
+
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
if (isStreaming && !open && !optedOut) setOpen(true);
|
|
167
|
+
}, [isStreaming, open, optedOut, setOpen]);
|
|
168
|
+
|
|
169
|
+
/*
|
|
170
|
+
* Once only. Without the latch, reopening a finished trace by hand would be
|
|
171
|
+
* undone a second later by the same effect, and the panel would refuse to
|
|
172
|
+
* stay open for the one reader who wanted to read it.
|
|
173
|
+
*/
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
if (!everStreamed.current || isStreaming || !open || autoClosed) return;
|
|
176
|
+
const timer = setTimeout(() => {
|
|
177
|
+
setOpen(false);
|
|
178
|
+
setAutoClosed(true);
|
|
179
|
+
}, AUTO_CLOSE_DELAY);
|
|
180
|
+
return () => clearTimeout(timer);
|
|
181
|
+
}, [isStreaming, open, autoClosed, setOpen]);
|
|
182
|
+
|
|
183
|
+
const context = useMemo(
|
|
184
|
+
() => ({ isStreaming, open, setOpen, duration: durationProp ?? measured }),
|
|
185
|
+
[isStreaming, open, setOpen, durationProp, measured]
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<ReasoningContext.Provider value={context}>
|
|
190
|
+
<View {...props} className={cn(root(), className)}>
|
|
191
|
+
{children}
|
|
192
|
+
</View>
|
|
193
|
+
</ReasoningContext.Provider>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
ReasoningRoot.displayName = 'Reasoning';
|
|
197
|
+
|
|
198
|
+
export interface ReasoningTriggerProps
|
|
199
|
+
extends Omit<PressableProps, 'children' | 'style'> {
|
|
200
|
+
className?: string;
|
|
201
|
+
/**
|
|
202
|
+
* What the row says. Given the streaming state and the measured duration, so
|
|
203
|
+
* a caller can put its own words to both without reimplementing the timing.
|
|
204
|
+
*/
|
|
205
|
+
label?: (isStreaming: boolean, duration?: number) => ReactNode;
|
|
206
|
+
/** Replaces the whole row, icon and chevron included. */
|
|
207
|
+
children?: ReactNode;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** The row that says how long it thought, and folds the trace away. */
|
|
211
|
+
function ReasoningTrigger({ className, label, children, onPress, ...props }: ReasoningTriggerProps) {
|
|
212
|
+
const { isStreaming, open, setOpen, duration } = useReasoning('Reasoning.Trigger');
|
|
213
|
+
const { trigger, label: labelClass } = reasoningVariants();
|
|
214
|
+
const reducedMotion = useReducedMotion();
|
|
215
|
+
const progress = useSharedValue(open ? 1 : 0);
|
|
216
|
+
|
|
217
|
+
useEffect(() => {
|
|
218
|
+
progress.value = reducedMotion
|
|
219
|
+
? open
|
|
220
|
+
? 1
|
|
221
|
+
: 0
|
|
222
|
+
: withTiming(open ? 1 : 0, { duration: 180 });
|
|
223
|
+
}, [open, reducedMotion, progress]);
|
|
224
|
+
|
|
225
|
+
const chevronStyle = useAnimatedStyle(() => ({
|
|
226
|
+
transform: [{ rotate: `${progress.value * 180}deg` }],
|
|
227
|
+
}));
|
|
228
|
+
|
|
229
|
+
const body = label ? (
|
|
230
|
+
label(isStreaming, duration)
|
|
231
|
+
) : isStreaming ? (
|
|
232
|
+
<Shimmer textClassName={labelClass()}>Thinking…</Shimmer>
|
|
233
|
+
) : (
|
|
234
|
+
<Text className={labelClass()}>
|
|
235
|
+
{duration === undefined
|
|
236
|
+
? 'Thought for a few seconds'
|
|
237
|
+
: `Thought for ${duration} ${duration === 1 ? 'second' : 'seconds'}`}
|
|
238
|
+
</Text>
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
return (
|
|
242
|
+
<Pressable
|
|
243
|
+
accessibilityRole="button"
|
|
244
|
+
accessibilityState={{ expanded: open }}
|
|
245
|
+
onPress={(event) => {
|
|
246
|
+
onPress?.(event);
|
|
247
|
+
setOpen(!open);
|
|
248
|
+
}}
|
|
249
|
+
className={cn(trigger(), className)}
|
|
250
|
+
{...props}
|
|
251
|
+
>
|
|
252
|
+
{children ?? (
|
|
253
|
+
<>
|
|
254
|
+
<SparklesIcon size={16} />
|
|
255
|
+
<View className="flex-1">{body}</View>
|
|
256
|
+
<Animated.View style={chevronStyle}>
|
|
257
|
+
<ChevronDownIcon size={16} />
|
|
258
|
+
</Animated.View>
|
|
259
|
+
</>
|
|
260
|
+
)}
|
|
261
|
+
</Pressable>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export interface ReasoningContentProps extends Omit<ViewProps, 'children'> {
|
|
266
|
+
className?: string;
|
|
267
|
+
/** The trace. A string is wrapped for you; anything else is left alone. */
|
|
268
|
+
children?: ReactNode;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* The trace itself.
|
|
273
|
+
*
|
|
274
|
+
* Kept mounted and collapsed to nothing rather than unmounted, because it is
|
|
275
|
+
* usually still growing while it is folded — and a body that remounts on every
|
|
276
|
+
* open would drop whatever the reader had scrolled to.
|
|
277
|
+
*/
|
|
278
|
+
function ReasoningContent({ className, children, ...props }: ReasoningContentProps) {
|
|
279
|
+
const { open } = useReasoning('Reasoning.Content');
|
|
280
|
+
const { content, contentText } = reasoningVariants();
|
|
281
|
+
|
|
282
|
+
return (
|
|
283
|
+
<Collapse open={open} className={cn(content(), className)} {...props}>
|
|
284
|
+
{textChildren(children, (text) => (
|
|
285
|
+
<Text className={contentText()}>{text}</Text>
|
|
286
|
+
))}
|
|
287
|
+
</Collapse>
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
ReasoningTrigger.displayName = 'Reasoning.Trigger';
|
|
292
|
+
ReasoningContent.displayName = 'Reasoning.Content';
|
|
293
|
+
|
|
294
|
+
export const Reasoning = Object.assign(ReasoningRoot, {
|
|
295
|
+
Trigger: ReasoningTrigger,
|
|
296
|
+
Content: ReasoningContent,
|
|
297
|
+
});
|
|
@@ -50,7 +50,7 @@ import Animated, {
|
|
|
50
50
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
51
51
|
import { useCSSVariable } from 'uniwind';
|
|
52
52
|
import { Portal } from '../../primitives/portal';
|
|
53
|
-
import { Text } from '../../primitives/text';
|
|
53
|
+
import { Text, textChildren } from '../../primitives/text';
|
|
54
54
|
import { cn } from '../../utils/cn';
|
|
55
55
|
import { selectionTick } from '../../utils/haptics';
|
|
56
56
|
|
|
@@ -242,7 +242,7 @@ function SectionRailRoot({
|
|
|
242
242
|
}}
|
|
243
243
|
{...props}
|
|
244
244
|
>
|
|
245
|
-
{children}
|
|
245
|
+
{textChildren(children)}
|
|
246
246
|
</View>
|
|
247
247
|
</SectionRailContext.Provider>
|
|
248
248
|
);
|
|
@@ -276,7 +276,7 @@ function SectionRailTrigger({ className, children, ...props }: SectionRailTrigge
|
|
|
276
276
|
)}
|
|
277
277
|
{...props}
|
|
278
278
|
>
|
|
279
|
-
{children}
|
|
279
|
+
{textChildren(children)}
|
|
280
280
|
</Pressable>
|
|
281
281
|
);
|
|
282
282
|
}
|
|
@@ -407,7 +407,7 @@ function SectionRailPanel({ className, children, ...props }: SectionRailContentP
|
|
|
407
407
|
{...props}
|
|
408
408
|
>
|
|
409
409
|
<ScrollView bounces={false} showsVerticalScrollIndicator={false}>
|
|
410
|
-
{children}
|
|
410
|
+
{textChildren(children)}
|
|
411
411
|
</ScrollView>
|
|
412
412
|
</Animated.View>
|
|
413
413
|
);
|
|
@@ -51,7 +51,8 @@ import { useCSSVariable } from 'uniwind';
|
|
|
51
51
|
import { CheckIcon, ChevronDownIcon } from '../../icons';
|
|
52
52
|
import { getNativeUI } from '../../native';
|
|
53
53
|
import { Portal } from '../../primitives/portal';
|
|
54
|
-
import { Text } from '../../primitives/text';
|
|
54
|
+
import { Text, textChildren } from '../../primitives/text';
|
|
55
|
+
import { useBackHandler } from '../../hooks/use-back-handler';
|
|
55
56
|
import { BottomSheet } from '../bottom-sheet';
|
|
56
57
|
|
|
57
58
|
const selectVariants = tv({
|
|
@@ -223,6 +224,11 @@ function SelectRoot({
|
|
|
223
224
|
onOpenChange?.(false);
|
|
224
225
|
}, [chevron, onOpenChange]);
|
|
225
226
|
|
|
227
|
+
// An open overlay list catches the Android back button, closing itself
|
|
228
|
+
// instead of popping the screen behind it. The `sheet` presentation gets the
|
|
229
|
+
// same behaviour from its BottomSheet; the native picker owns its own back.
|
|
230
|
+
useBackHandler(open && presentation === 'overlay' && !nativeUI, close);
|
|
231
|
+
|
|
226
232
|
const toggle = useCallback(() => {
|
|
227
233
|
if (open) {
|
|
228
234
|
close();
|
|
@@ -333,7 +339,7 @@ function SelectRoot({
|
|
|
333
339
|
</Text>
|
|
334
340
|
) : null}
|
|
335
341
|
<ScrollView bounces={false} className="max-h-96">
|
|
336
|
-
<View className="gap-1 pb-2">{children}</View>
|
|
342
|
+
<View className="gap-1 pb-2">{textChildren(children)}</View>
|
|
337
343
|
</ScrollView>
|
|
338
344
|
</SelectContext.Provider>
|
|
339
345
|
</BottomSheet.Content>
|
|
@@ -353,7 +359,7 @@ function SelectRoot({
|
|
|
353
359
|
exiting={FadeOut.duration(120)}
|
|
354
360
|
className={slots.list()}
|
|
355
361
|
>
|
|
356
|
-
{children}
|
|
362
|
+
{textChildren(children)}
|
|
357
363
|
</Animated.View>
|
|
358
364
|
) : null}
|
|
359
365
|
</View>
|
|
@@ -410,7 +416,7 @@ function SelectRoot({
|
|
|
410
416
|
className={slots.list()}
|
|
411
417
|
>
|
|
412
418
|
<ScrollView bounces={false} showsVerticalScrollIndicator={false}>
|
|
413
|
-
{children}
|
|
419
|
+
{textChildren(children)}
|
|
414
420
|
</ScrollView>
|
|
415
421
|
</Animated.View>
|
|
416
422
|
</SelectContext.Provider>
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sources — where an answer came from, folded under a count.
|
|
3
|
+
*
|
|
4
|
+
* A cited answer usually cites more places than anyone wants listed above it,
|
|
5
|
+
* so the list opens from a single line: "Used 6 sources". Folded is the resting
|
|
6
|
+
* state, unlike `Reasoning`, because a citation list is a thing you go and
|
|
7
|
+
* check rather than a thing you watch arrive.
|
|
8
|
+
*
|
|
9
|
+
* ```tsx
|
|
10
|
+
* <Sources>
|
|
11
|
+
* <Sources.Trigger count={sources.length} />
|
|
12
|
+
* <Sources.Content>
|
|
13
|
+
* {sources.map((source) => (
|
|
14
|
+
* <Sources.Source key={source.url} href={source.url} title={source.title} />
|
|
15
|
+
* ))}
|
|
16
|
+
* </Sources.Content>
|
|
17
|
+
* </Sources>
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* ## Where the props come from
|
|
21
|
+
*
|
|
22
|
+
* With the AI SDK these are the `source-url` parts of an assistant message:
|
|
23
|
+
* `part.url` is the `href` and `part.title` the `title`. A part carries no
|
|
24
|
+
* title of its own when the model did not supply one, in which case the host
|
|
25
|
+
* of the URL is a better label than the whole of it.
|
|
26
|
+
*/
|
|
27
|
+
import {
|
|
28
|
+
createContext,
|
|
29
|
+
useCallback,
|
|
30
|
+
useContext,
|
|
31
|
+
useEffect,
|
|
32
|
+
useMemo,
|
|
33
|
+
useState,
|
|
34
|
+
type ReactNode,
|
|
35
|
+
} from 'react';
|
|
36
|
+
import {
|
|
37
|
+
Linking,
|
|
38
|
+
Pressable,
|
|
39
|
+
View,
|
|
40
|
+
type PressableProps,
|
|
41
|
+
type ViewProps,
|
|
42
|
+
} 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, LinkIcon } from '../../icons';
|
|
51
|
+
import { Collapse } from '../../primitives/collapse';
|
|
52
|
+
import { Text } from '../../primitives/text';
|
|
53
|
+
import { cn } from '../../utils/cn';
|
|
54
|
+
|
|
55
|
+
const sourcesVariants = tv({
|
|
56
|
+
slots: {
|
|
57
|
+
root: 'w-full gap-2',
|
|
58
|
+
trigger: 'flex-row items-center gap-2 py-0.5',
|
|
59
|
+
triggerLabel: 'text-sm font-medium text-muted-foreground',
|
|
60
|
+
content: 'gap-0.5',
|
|
61
|
+
source: 'flex-row items-center gap-2 rounded-xl px-2 py-2 active:bg-accent',
|
|
62
|
+
sourceTitle: 'flex-1 text-sm text-foreground',
|
|
63
|
+
sourceHost: 'text-xs text-muted-foreground',
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
interface SourcesContextValue {
|
|
68
|
+
open: boolean;
|
|
69
|
+
setOpen: (open: boolean) => void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const SourcesContext = createContext<SourcesContextValue | null>(null);
|
|
73
|
+
|
|
74
|
+
function useSources(component: string): SourcesContextValue {
|
|
75
|
+
const context = useContext(SourcesContext);
|
|
76
|
+
if (!context) {
|
|
77
|
+
throw new Error(`${component} must be used within a <Sources>`);
|
|
78
|
+
}
|
|
79
|
+
return context;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface SourcesProps extends Omit<ViewProps, 'children'> {
|
|
83
|
+
className?: string;
|
|
84
|
+
/** Controlled open state. */
|
|
85
|
+
open?: boolean;
|
|
86
|
+
/** Initial state when uncontrolled. Folded, which is where a citation list belongs. */
|
|
87
|
+
defaultOpen?: boolean;
|
|
88
|
+
onOpenChange?: (open: boolean) => void;
|
|
89
|
+
children?: ReactNode;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function SourcesRoot({
|
|
93
|
+
className,
|
|
94
|
+
open: openProp,
|
|
95
|
+
defaultOpen = false,
|
|
96
|
+
onOpenChange,
|
|
97
|
+
children,
|
|
98
|
+
...props
|
|
99
|
+
}: SourcesProps) {
|
|
100
|
+
const { root } = sourcesVariants();
|
|
101
|
+
const [internalOpen, setInternalOpen] = useState(defaultOpen);
|
|
102
|
+
const isControlled = openProp !== undefined;
|
|
103
|
+
const open = isControlled ? openProp : internalOpen;
|
|
104
|
+
|
|
105
|
+
const setOpen = useCallback(
|
|
106
|
+
(next: boolean) => {
|
|
107
|
+
if (!isControlled) setInternalOpen(next);
|
|
108
|
+
onOpenChange?.(next);
|
|
109
|
+
},
|
|
110
|
+
[isControlled, onOpenChange]
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const context = useMemo(() => ({ open, setOpen }), [open, setOpen]);
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<SourcesContext.Provider value={context}>
|
|
117
|
+
<View {...props} className={cn(root(), className)}>
|
|
118
|
+
{children}
|
|
119
|
+
</View>
|
|
120
|
+
</SourcesContext.Provider>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
SourcesRoot.displayName = 'Sources';
|
|
124
|
+
|
|
125
|
+
export interface SourcesTriggerProps
|
|
126
|
+
extends Omit<PressableProps, 'children' | 'style'> {
|
|
127
|
+
className?: string;
|
|
128
|
+
/** How many sources the list holds. Reads "Used 6 sources". */
|
|
129
|
+
count?: number;
|
|
130
|
+
/** Replaces the whole row. */
|
|
131
|
+
children?: ReactNode;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function SourcesTrigger({
|
|
135
|
+
className,
|
|
136
|
+
count = 0,
|
|
137
|
+
children,
|
|
138
|
+
onPress,
|
|
139
|
+
...props
|
|
140
|
+
}: SourcesTriggerProps) {
|
|
141
|
+
const { open, setOpen } = useSources('Sources.Trigger');
|
|
142
|
+
const { trigger, triggerLabel } = sourcesVariants();
|
|
143
|
+
const reducedMotion = useReducedMotion();
|
|
144
|
+
const progress = useSharedValue(open ? 1 : 0);
|
|
145
|
+
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
progress.value = reducedMotion
|
|
148
|
+
? open
|
|
149
|
+
? 1
|
|
150
|
+
: 0
|
|
151
|
+
: withTiming(open ? 1 : 0, { duration: 180 });
|
|
152
|
+
}, [open, reducedMotion, progress]);
|
|
153
|
+
|
|
154
|
+
const chevronStyle = useAnimatedStyle(() => ({
|
|
155
|
+
transform: [{ rotate: `${progress.value * 180}deg` }],
|
|
156
|
+
}));
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<Pressable
|
|
160
|
+
accessibilityRole="button"
|
|
161
|
+
accessibilityState={{ expanded: open }}
|
|
162
|
+
onPress={(event) => {
|
|
163
|
+
onPress?.(event);
|
|
164
|
+
setOpen(!open);
|
|
165
|
+
}}
|
|
166
|
+
className={cn(trigger(), className)}
|
|
167
|
+
{...props}
|
|
168
|
+
>
|
|
169
|
+
{children ?? (
|
|
170
|
+
<>
|
|
171
|
+
<Text className={triggerLabel()}>
|
|
172
|
+
Used {count} {count === 1 ? 'source' : 'sources'}
|
|
173
|
+
</Text>
|
|
174
|
+
<Animated.View style={chevronStyle}>
|
|
175
|
+
<ChevronDownIcon size={16} />
|
|
176
|
+
</Animated.View>
|
|
177
|
+
</>
|
|
178
|
+
)}
|
|
179
|
+
</Pressable>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export interface SourcesContentProps extends Omit<ViewProps, 'children'> {
|
|
184
|
+
className?: string;
|
|
185
|
+
children?: ReactNode;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function SourcesContent({ className, children, ...props }: SourcesContentProps) {
|
|
189
|
+
const { open } = useSources('Sources.Content');
|
|
190
|
+
const { content } = sourcesVariants();
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<Collapse open={open} className={cn(content(), className)} {...props}>
|
|
194
|
+
{children}
|
|
195
|
+
</Collapse>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export interface SourcesSourceProps
|
|
200
|
+
extends Omit<PressableProps, 'children' | 'style'> {
|
|
201
|
+
className?: string;
|
|
202
|
+
/** Where the row goes. Opened with the platform's own handler. */
|
|
203
|
+
href?: string;
|
|
204
|
+
/**
|
|
205
|
+
* What the row says. Falls back to the URL's host, which is a better label
|
|
206
|
+
* than a hundred characters of path — and models often send no title at all.
|
|
207
|
+
*/
|
|
208
|
+
title?: string;
|
|
209
|
+
/** Leading glyph. A link by default; a favicon suits a real citation list. */
|
|
210
|
+
icon?: ReactNode;
|
|
211
|
+
children?: ReactNode;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** One citation. */
|
|
215
|
+
function SourcesSource({
|
|
216
|
+
className,
|
|
217
|
+
href,
|
|
218
|
+
title,
|
|
219
|
+
icon,
|
|
220
|
+
children,
|
|
221
|
+
onPress,
|
|
222
|
+
...props
|
|
223
|
+
}: SourcesSourceProps) {
|
|
224
|
+
const { source, sourceTitle, sourceHost } = sourcesVariants();
|
|
225
|
+
const host = useMemo(() => hostOf(href), [href]);
|
|
226
|
+
|
|
227
|
+
return (
|
|
228
|
+
<Pressable
|
|
229
|
+
accessibilityRole="link"
|
|
230
|
+
accessibilityLabel={title ?? host ?? href}
|
|
231
|
+
onPress={(event) => {
|
|
232
|
+
onPress?.(event);
|
|
233
|
+
// No `canOpenURL` guard: it needs the scheme declared up front on iOS,
|
|
234
|
+
// and a source is always http(s), which is always openable.
|
|
235
|
+
if (href) void Linking.openURL(href).catch(() => undefined);
|
|
236
|
+
}}
|
|
237
|
+
className={cn(source(), className)}
|
|
238
|
+
{...props}
|
|
239
|
+
>
|
|
240
|
+
{children ?? (
|
|
241
|
+
<>
|
|
242
|
+
{icon ?? <LinkIcon size={14} />}
|
|
243
|
+
<Text numberOfLines={1} className={sourceTitle()}>
|
|
244
|
+
{title ?? host ?? href}
|
|
245
|
+
</Text>
|
|
246
|
+
{title && host ? (
|
|
247
|
+
<Text numberOfLines={1} className={sourceHost()}>
|
|
248
|
+
{host}
|
|
249
|
+
</Text>
|
|
250
|
+
) : null}
|
|
251
|
+
</>
|
|
252
|
+
)}
|
|
253
|
+
</Pressable>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* The host of a URL, without a URL parser.
|
|
259
|
+
*
|
|
260
|
+
* `URL` exists in Hermes but throws on anything malformed, and a model's
|
|
261
|
+
* citation is not a thing to trust with an exception. A label is worth having
|
|
262
|
+
* even when the string it came from is not a URL at all.
|
|
263
|
+
*/
|
|
264
|
+
function hostOf(href: string | undefined): string | undefined {
|
|
265
|
+
if (!href) return undefined;
|
|
266
|
+
const match = /^[a-z][a-z0-9+.-]*:\/\/([^/?#]+)/i.exec(href);
|
|
267
|
+
return match?.[1]?.replace(/^www\./i, '');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
SourcesTrigger.displayName = 'Sources.Trigger';
|
|
271
|
+
SourcesContent.displayName = 'Sources.Content';
|
|
272
|
+
SourcesSource.displayName = 'Sources.Source';
|
|
273
|
+
|
|
274
|
+
export const Sources = Object.assign(SourcesRoot, {
|
|
275
|
+
Trigger: SourcesTrigger,
|
|
276
|
+
Content: SourcesContent,
|
|
277
|
+
Source: SourcesSource,
|
|
278
|
+
});
|
|
@@ -26,7 +26,7 @@ import { Pressable, View, type Text as RNText, type ViewProps } from 'react-nati
|
|
|
26
26
|
import { tv } from 'tailwind-variants';
|
|
27
27
|
import { useCSSVariable } from 'uniwind';
|
|
28
28
|
import { CheckIcon } from '../../icons';
|
|
29
|
-
import { Text, type TextProps } from '../../primitives/text';
|
|
29
|
+
import { Text, type TextProps, textChildren } from '../../primitives/text';
|
|
30
30
|
import { Spinner } from '../spinner';
|
|
31
31
|
|
|
32
32
|
export type StepState = 'active' | 'completed' | 'inactive' | 'loading';
|
|
@@ -163,7 +163,7 @@ const StepsRoot = forwardRef<View, StepsProps>(
|
|
|
163
163
|
return (
|
|
164
164
|
<StepsContext.Provider value={context}>
|
|
165
165
|
<View ref={ref} className={root({ className })} {...props}>
|
|
166
|
-
{children}
|
|
166
|
+
{textChildren(children)}
|
|
167
167
|
</View>
|
|
168
168
|
</StepsContext.Provider>
|
|
169
169
|
);
|
|
@@ -210,7 +210,7 @@ const StepsItem = forwardRef<View, StepsItemProps>(
|
|
|
210
210
|
return (
|
|
211
211
|
<StepItemContext.Provider value={context}>
|
|
212
212
|
<View ref={ref} className={item({ className })} {...props}>
|
|
213
|
-
{children}
|
|
213
|
+
{textChildren(children)}
|
|
214
214
|
</View>
|
|
215
215
|
</StepItemContext.Provider>
|
|
216
216
|
);
|
|
@@ -240,7 +240,7 @@ const StepsTrigger = forwardRef<View, StepsTriggerProps>(
|
|
|
240
240
|
className={trigger({ className })}
|
|
241
241
|
{...props}
|
|
242
242
|
>
|
|
243
|
-
{children}
|
|
243
|
+
{textChildren(children)}
|
|
244
244
|
</Pressable>
|
|
245
245
|
);
|
|
246
246
|
}
|