@vvedantb/native-ui 0.17.2 → 0.18.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/dist/AlertDialog.jsx +123 -0
- package/dist/AmbientBackground.jsx +73 -0
- package/dist/ArtifactList.jsx +59 -0
- package/dist/Avatar.jsx +285 -0
- package/dist/Badge.jsx +154 -0
- package/dist/BottomSheetContainer.jsx +208 -0
- package/dist/BulkActionBar.jsx +717 -0
- package/dist/Button.jsx +344 -0
- package/dist/CaptureButton.jsx +104 -0
- package/dist/Card.jsx +371 -0
- package/dist/Checkbox.jsx +177 -0
- package/dist/Chip.jsx +79 -0
- package/dist/ColorSwatches.jsx +159 -0
- package/dist/ConfirmDialog.jsx +157 -0
- package/dist/DateTimeField.jsx +204 -0
- package/dist/Dialog.jsx +614 -0
- package/dist/DividedList.jsx +120 -0
- package/dist/DropdownMenu.jsx +849 -0
- package/dist/EmptyState.jsx +434 -0
- package/dist/ExpoWebRoot.jsx +134 -0
- package/dist/FloatingFAB.jsx +95 -0
- package/dist/FloatingSurface.jsx +226 -0
- package/dist/FloatingTabBar.jsx +403 -0
- package/dist/HeaderIconButton.jsx +53 -0
- package/dist/IconButton.jsx +157 -0
- package/dist/Input.jsx +96 -0
- package/dist/Label.jsx +96 -0
- package/dist/LabeledSwitchRow.jsx +170 -0
- package/dist/LiquidGlass.jsx +2 -0
- package/dist/ListRow.jsx +281 -0
- package/dist/LoadingState.jsx +205 -0
- package/dist/PageIndicator.jsx +128 -0
- package/dist/PageTitle.jsx +205 -0
- package/dist/Pager.jsx +105 -0
- package/dist/PasswordInput.jsx +120 -0
- package/dist/PillTabs.jsx +300 -0
- package/dist/PinnedBotTile.jsx +212 -0
- package/dist/Progress.jsx +147 -0
- package/dist/SafeAreaContainer.jsx +38 -0
- package/dist/ScreenContainer.jsx +117 -0
- package/dist/SearchInput.jsx +150 -0
- package/dist/SectionLabel.jsx +128 -0
- package/dist/Select.jsx +223 -0
- package/dist/Separator.jsx +95 -0
- package/dist/Sheet.jsx +343 -0
- package/dist/SheetRow.jsx +198 -0
- package/dist/Skeleton.jsx +311 -0
- package/dist/Spinner.jsx +195 -0
- package/dist/StackPageHeader.jsx +160 -0
- package/dist/StatGrid.jsx +74 -0
- package/dist/StatusDot.jsx +129 -0
- package/dist/SurfaceRow.jsx +143 -0
- package/dist/SwipeableRow.jsx +50 -0
- package/dist/Switch.jsx +130 -0
- package/dist/Tabs.jsx +385 -0
- package/dist/Textarea.jsx +103 -0
- package/dist/ThemeToggle.jsx +78 -0
- package/dist/Toast.jsx +396 -0
- package/dist/ai/activity-step-detail.jsx +179 -0
- package/dist/ai/activity-step-label.js +816 -0
- package/dist/ai/activity-step-types.js +14 -0
- package/dist/ai/activity-step.jsx +98 -0
- package/dist/ai/activity-tasks-utils.js +172 -0
- package/dist/ai/activity-timeline.js +47 -0
- package/dist/ai/attachments.jsx +258 -0
- package/dist/ai/card-button.jsx +188 -0
- package/dist/ai/chain-of-thought.jsx +459 -0
- package/dist/ai/chat-jump-rail.jsx +57 -0
- package/dist/ai/chat-text.js +44 -0
- package/dist/ai/chat-thread.jsx +135 -0
- package/dist/ai/choice-question.jsx +925 -0
- package/dist/ai/code-block.jsx +574 -0
- package/dist/ai/collapsible.jsx +275 -0
- package/dist/ai/conversation.jsx +723 -0
- package/dist/ai/file-chip.jsx +198 -0
- package/dist/ai/icon-button.jsx +179 -0
- package/dist/ai/index.js +37 -0
- package/dist/ai/message-action-sheet.jsx +408 -0
- package/dist/ai/message-quote.jsx +241 -0
- package/dist/ai/message.jsx +739 -0
- package/dist/ai/prompt-input-menu.jsx +365 -0
- package/dist/ai/prompt-input.jsx +626 -0
- package/dist/ai/reasoning.jsx +446 -0
- package/dist/ai/reply-quote-chip.jsx +278 -0
- package/dist/ai/response-text.jsx +130 -0
- package/dist/ai/shimmer.jsx +149 -0
- package/dist/ai/sources.jsx +260 -0
- package/dist/ai/subagent-cards.jsx +645 -0
- package/dist/ai/suggestion.jsx +186 -0
- package/dist/ai/tool-approval-card.jsx +359 -0
- package/dist/ai/trace.jsx +728 -0
- package/dist/ai/unified-diff.jsx +280 -0
- package/dist/ai/use-streaming-disclosure.js +70 -0
- package/dist/auth/AuthProviders.jsx +27 -0
- package/dist/auth/AuthScreen.jsx +292 -0
- package/dist/auth/GoogleSignInButton.jsx +188 -0
- package/dist/auth/SecondFactorScreen.jsx +133 -0
- package/dist/auth/SignInScreen.jsx +482 -0
- package/dist/auth/authErrors.js +131 -0
- package/dist/auth/index.js +11 -0
- package/dist/auth/totp.js +378 -0
- package/dist/blur.js +36 -0
- package/dist/brand/AppIcons.jsx +118 -0
- package/dist/brand/AppMark.jsx +128 -0
- package/dist/brand/AppWordmark.jsx +138 -0
- package/dist/brand/GoogleMark.jsx +71 -0
- package/dist/brand/ProviderIcon.jsx +192 -0
- package/dist/brand/familyApps.js +33 -0
- package/dist/brand/index.js +11 -0
- package/dist/brand/svg.js +252 -0
- package/dist/brand/vProAppBenefits.js +19 -0
- package/dist/cn.js +5 -0
- package/dist/colors.js +15 -0
- package/dist/configureTheme.js +72 -0
- package/dist/controlStyles.js +520 -0
- package/dist/definiteSize.js +12 -0
- package/dist/dialogLayout.js +18 -0
- package/dist/entries/ai.js +4 -0
- package/dist/entries/ambient.js +3 -0
- package/dist/entries/auth.js +5 -0
- package/dist/entries/bottom-sheet.js +2 -0
- package/dist/entries/brand.js +2 -0
- package/dist/entries/button.js +2 -0
- package/dist/entries/date-time.js +3 -0
- package/dist/entries/expo-router.js +4 -0
- package/dist/entries/expo-web-root.js +3 -0
- package/dist/entries/floating.js +7 -0
- package/dist/entries/gesture-handler.js +4 -0
- package/dist/entries/keyboard-controller.js +4 -0
- package/dist/entries/layout.js +16 -0
- package/dist/entries/markdown.js +4 -0
- package/dist/entries/marketing.js +3 -0
- package/dist/entries/page-title.js +7 -0
- package/dist/entries/password-input.js +3 -0
- package/dist/entries/primitives.js +57 -0
- package/dist/entries/search-input.js +3 -0
- package/dist/entries/tab-bar.js +4 -0
- package/dist/entries/theme.js +7 -0
- package/dist/entries/toast.js +5 -0
- package/dist/entries/video.js +3 -0
- package/dist/entries/webview.js +4 -0
- package/dist/examples/AiChatGallery.jsx +509 -0
- package/dist/examples/ParityGallery.jsx +1307 -0
- package/dist/floating.js +62 -0
- package/dist/glassBlur.jsx +145 -0
- package/dist/gradient.js +17 -0
- package/dist/haptics.js +44 -0
- package/dist/highlight.js +382 -0
- package/dist/index.js +85 -0
- package/dist/initials.js +4 -0
- package/dist/loadingGrid.js +81 -0
- package/dist/markdown/Markdown.jsx +515 -0
- package/dist/markdown/MarkdownTable.jsx +443 -0
- package/dist/markdown/tableText.js +68 -0
- package/dist/marketing/PricingModal.jsx +608 -0
- package/dist/marketing/UpgradeToProCard.jsx +164 -0
- package/dist/marketing/pricing.js +34 -0
- package/dist/media/ImageGallery.jsx +580 -0
- package/dist/media/MediaCard.jsx +162 -0
- package/dist/media/imageGrid.js +54 -0
- package/dist/motion.js +33 -0
- package/dist/pagerSwipe.js +104 -0
- package/dist/palette.js +104 -0
- package/dist/primitives/button.jsx +127 -0
- package/dist/primitives/card.jsx +249 -0
- package/dist/primitives/icon.jsx +37 -0
- package/dist/primitives/input.jsx +56 -0
- package/dist/primitives/keyboard-controller.jsx +207 -0
- package/dist/primitives/keyboard-parts.jsx +75 -0
- package/dist/primitives/native-only-animated-view.jsx +68 -0
- package/dist/primitives/overlay.jsx +391 -0
- package/dist/primitives/pager-gesture.jsx +131 -0
- package/dist/primitives/select-host.jsx +103 -0
- package/dist/primitives/select.jsx +555 -0
- package/dist/primitives/sheet-drag.jsx +420 -0
- package/dist/primitives/sheet-gesture.jsx +219 -0
- package/dist/primitives/swipe-row-gesture.jsx +341 -0
- package/dist/primitives/switch.jsx +105 -0
- package/dist/primitives/text.jsx +145 -0
- package/dist/primitives/toast-drag-gesture.jsx +94 -0
- package/dist/primitives/use-controllable-state.js +40 -0
- package/dist/primitives/web-click-guard.js +69 -0
- package/dist/push.js +166 -0
- package/dist/relativeTime.js +70 -0
- package/dist/router/RouterStackPageHeader.jsx +53 -0
- package/dist/selectA11y.js +32 -0
- package/dist/settings/MoreFromProjectV.jsx +168 -0
- package/dist/settings/SettingsEmptyState.jsx +102 -0
- package/dist/settings/SettingsField.jsx +49 -0
- package/dist/settings/SettingsSection.jsx +101 -0
- package/dist/settings/SettingsStack.jsx +17 -0
- package/dist/settings/SettingsToggleRow.jsx +163 -0
- package/dist/settings/projectVMobileApps.js +126 -0
- package/dist/settings/relaxedSizeStyle.js +11 -0
- package/dist/sheetDrag.js +76 -0
- package/dist/svgA11y.js +46 -0
- package/dist/swipeRow.js +161 -0
- package/dist/tabBarRoutes.js +41 -0
- package/dist/tabIndicator.js +139 -0
- package/dist/tailwind-preset.cjs +255 -0
- package/dist/tailwind-preset.d.cts +55 -0
- package/dist/theme.jsx +62 -0
- package/dist/toastChrome.js +110 -0
- package/dist/toastDrag.js +51 -0
- package/dist/toastStore.js +54 -0
- package/dist/tokens.js +70 -0
- package/dist/typography.js +40 -0
- package/dist/useReduceMotion.js +32 -0
- package/dist/useReduceTransparency.js +27 -0
- package/dist/useScheme.js +15 -0
- package/dist/video/InlineVideo.jsx +115 -0
- package/dist/webview/HtmlView.jsx +169 -0
- package/dist/webview/HtmlView.web.jsx +63 -0
- package/dist/webview/htmlView.js +43 -0
- package/package.json +70 -33
- package/src/DropdownMenu.tsx +28 -16
- package/src/FloatingTabBar.tsx +78 -41
- package/src/ai/conversation.tsx +12 -8
- package/src/glassBlur.tsx +47 -22
- package/src/primitives/swipe-row-gesture.tsx +11 -2
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
import { c as _c } from "react/compiler-runtime";
|
|
2
|
+
import { createContext, isValidElement, useContext, useRef, useState } from "react";
|
|
3
|
+
import { FlatList, Platform, Pressable, ScrollView, StyleSheet, Text, View } from "react-native";
|
|
4
|
+
import { IconArrowDown } from "@tabler/icons-react-native";
|
|
5
|
+
import { FadeIn, FadeOut } from "react-native-reanimated";
|
|
6
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
7
|
+
import { EASE_OUT } from "../motion";
|
|
8
|
+
import { Button } from "../Button";
|
|
9
|
+
import { cn } from "../cn";
|
|
10
|
+
import { MIN_TOUCH_TARGET, sizeStyle, textStyle, touchSlop, typeStyle } from "../controlStyles";
|
|
11
|
+
import { withAlpha } from "../palette";
|
|
12
|
+
import { KeyboardLiftContext, useKeyboardParts } from "../primitives/keyboard-parts";
|
|
13
|
+
import { NativeOnlyAnimatedView } from "../primitives/native-only-animated-view";
|
|
14
|
+
import { useLargerText } from "../theme";
|
|
15
|
+
import { tokens } from "../tokens";
|
|
16
|
+
import { useReduceMotion } from "../useReduceMotion";
|
|
17
|
+
import { useScheme } from "../useScheme";
|
|
18
|
+
import { chatTextSize } from "./chat-text";
|
|
19
|
+
|
|
20
|
+
/** Within this many points of the end, the thread counts as at the bottom and keeps following new content. */
|
|
21
|
+
export const AT_BOTTOM_THRESHOLD = 70;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Whether a scroll position is within `AT_BOTTOM_THRESHOLD` of the end.
|
|
25
|
+
* Content shorter than the view is at the bottom. `bottomInset` is space
|
|
26
|
+
* added past the content (the keyboard's, with `KeyboardControllerProvider`).
|
|
27
|
+
* An `inverted` list (newest first) has its end at offset 0, and there
|
|
28
|
+
* `bottomInset` is space added before offset 0 (the keyboard's on iOS), so
|
|
29
|
+
* the end is at minus `bottomInset`.
|
|
30
|
+
*/
|
|
31
|
+
export function isScrolledToBottom({
|
|
32
|
+
contentOffset,
|
|
33
|
+
contentSize,
|
|
34
|
+
layoutMeasurement
|
|
35
|
+
}, bottomInset = 0, inverted = false) {
|
|
36
|
+
if (inverted) return contentOffset.y + bottomInset <= AT_BOTTOM_THRESHOLD;
|
|
37
|
+
return contentSize.height + bottomInset - layoutMeasurement.height - contentOffset.y <= AT_BOTTOM_THRESHOLD;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* What the thread scrolls with: the `ScrollView` of `ConversationContent` or
|
|
42
|
+
* the `FlatList` of `ConversationList`. `scrollToEnd` goes to the newest
|
|
43
|
+
* message, which is offset 0 in an inverted list. `scrollTo` goes to an
|
|
44
|
+
* offset; only the `ScrollView` has it, so call it as `scrollTo?.(...)`.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
const ConversationContext = createContext(null);
|
|
48
|
+
|
|
49
|
+
/** `isAtBottom` and `scrollToBottom` of the enclosing `Conversation`, for a custom scroll button. */
|
|
50
|
+
export function useConversation() {
|
|
51
|
+
const context = useContext(ConversationContext);
|
|
52
|
+
if (!context) {
|
|
53
|
+
throw new Error("Conversation parts must be used within Conversation");
|
|
54
|
+
}
|
|
55
|
+
return context;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The thread frame (web `relative flex-1`). It sticks to the bottom: while the
|
|
59
|
+
* reader is at the end, new and growing messages stay in view; once they
|
|
60
|
+
* scroll up, it stops following and `ConversationScrollButton` shows.
|
|
61
|
+
*/
|
|
62
|
+
export function Conversation(t0) {
|
|
63
|
+
const $ = _c(21);
|
|
64
|
+
let className;
|
|
65
|
+
let props;
|
|
66
|
+
if ($[0] !== t0) {
|
|
67
|
+
({
|
|
68
|
+
className,
|
|
69
|
+
...props
|
|
70
|
+
} = t0);
|
|
71
|
+
$[0] = t0;
|
|
72
|
+
$[1] = className;
|
|
73
|
+
$[2] = props;
|
|
74
|
+
} else {
|
|
75
|
+
className = $[1];
|
|
76
|
+
props = $[2];
|
|
77
|
+
}
|
|
78
|
+
const reduceMotion = useReduceMotion();
|
|
79
|
+
const scrollRef = useRef(null);
|
|
80
|
+
const atBottom = useRef(true);
|
|
81
|
+
const autoScrolling = useRef(false);
|
|
82
|
+
const [isAtBottom, setIsAtBottom] = useState(true);
|
|
83
|
+
const [dockHeight, setDockHeight] = useState(0);
|
|
84
|
+
let t1;
|
|
85
|
+
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
|
86
|
+
t1 = next => {
|
|
87
|
+
atBottom.current = next;
|
|
88
|
+
setIsAtBottom(next);
|
|
89
|
+
};
|
|
90
|
+
$[3] = t1;
|
|
91
|
+
} else {
|
|
92
|
+
t1 = $[3];
|
|
93
|
+
}
|
|
94
|
+
const update = t1;
|
|
95
|
+
let t2;
|
|
96
|
+
if ($[4] !== reduceMotion) {
|
|
97
|
+
t2 = () => {
|
|
98
|
+
autoScrolling.current = true;
|
|
99
|
+
update(true);
|
|
100
|
+
scrollRef.current?.scrollToEnd({
|
|
101
|
+
animated: !reduceMotion
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
$[4] = reduceMotion;
|
|
105
|
+
$[5] = t2;
|
|
106
|
+
} else {
|
|
107
|
+
t2 = $[5];
|
|
108
|
+
}
|
|
109
|
+
let t3;
|
|
110
|
+
let t4;
|
|
111
|
+
let t5;
|
|
112
|
+
if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
|
|
113
|
+
t3 = (event, bottomInset, inverted) => {
|
|
114
|
+
const bottom = isScrolledToBottom(event, bottomInset, inverted);
|
|
115
|
+
if (bottom) {
|
|
116
|
+
autoScrolling.current = false;
|
|
117
|
+
} else {
|
|
118
|
+
if (autoScrolling.current) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
update(bottom);
|
|
123
|
+
};
|
|
124
|
+
t4 = () => {
|
|
125
|
+
autoScrolling.current = false;
|
|
126
|
+
};
|
|
127
|
+
t5 = () => {
|
|
128
|
+
if (atBottom.current) {
|
|
129
|
+
scrollRef.current?.scrollToEnd({
|
|
130
|
+
animated: false
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
$[6] = t3;
|
|
135
|
+
$[7] = t4;
|
|
136
|
+
$[8] = t5;
|
|
137
|
+
} else {
|
|
138
|
+
t3 = $[6];
|
|
139
|
+
t4 = $[7];
|
|
140
|
+
t5 = $[8];
|
|
141
|
+
}
|
|
142
|
+
let t6;
|
|
143
|
+
if ($[9] !== dockHeight || $[10] !== isAtBottom || $[11] !== t2) {
|
|
144
|
+
t6 = {
|
|
145
|
+
isAtBottom,
|
|
146
|
+
scrollRef,
|
|
147
|
+
scrollToBottom: t2,
|
|
148
|
+
onScroll: t3,
|
|
149
|
+
onUserScroll: t4,
|
|
150
|
+
followContent: t5,
|
|
151
|
+
dockHeight,
|
|
152
|
+
setDockHeight
|
|
153
|
+
};
|
|
154
|
+
$[9] = dockHeight;
|
|
155
|
+
$[10] = isAtBottom;
|
|
156
|
+
$[11] = t2;
|
|
157
|
+
$[12] = t6;
|
|
158
|
+
} else {
|
|
159
|
+
t6 = $[12];
|
|
160
|
+
}
|
|
161
|
+
const value = t6;
|
|
162
|
+
let t7;
|
|
163
|
+
if ($[13] !== className) {
|
|
164
|
+
t7 = cn("relative flex-1", className);
|
|
165
|
+
$[13] = className;
|
|
166
|
+
$[14] = t7;
|
|
167
|
+
} else {
|
|
168
|
+
t7 = $[14];
|
|
169
|
+
}
|
|
170
|
+
let t8;
|
|
171
|
+
if ($[15] !== props || $[16] !== t7) {
|
|
172
|
+
t8 = <View className={t7} {...props} />;
|
|
173
|
+
$[15] = props;
|
|
174
|
+
$[16] = t7;
|
|
175
|
+
$[17] = t8;
|
|
176
|
+
} else {
|
|
177
|
+
t8 = $[17];
|
|
178
|
+
}
|
|
179
|
+
let t9;
|
|
180
|
+
if ($[18] !== t8 || $[19] !== value) {
|
|
181
|
+
t9 = <ConversationContext.Provider value={value}>{t8}</ConversationContext.Provider>;
|
|
182
|
+
$[18] = t8;
|
|
183
|
+
$[19] = value;
|
|
184
|
+
$[20] = t9;
|
|
185
|
+
} else {
|
|
186
|
+
t9 = $[20];
|
|
187
|
+
}
|
|
188
|
+
return t9;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** The scroll handlers a thread's scroller passes on, as `ScrollView` and `FlatList` both take them. */
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Taps reach rows while the keyboard is up; a drag lowers it (interactively
|
|
195
|
+
* on iOS, and on Android 11+ with `KeyboardControllerProvider`, in
|
|
196
|
+
* `ConversationList`); scroll events at 60 Hz.
|
|
197
|
+
*/
|
|
198
|
+
const SCROLL_DEFAULTS = {
|
|
199
|
+
keyboardShouldPersistTaps: "handled",
|
|
200
|
+
keyboardDismissMode: Platform.OS === "ios" ? "interactive" : "on-drag",
|
|
201
|
+
scrollEventThrottle: 16
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* The wiring `ConversationContent` and `ConversationList` share: the ref the
|
|
206
|
+
* scroll button and the follow use, taps that reach rows while the keyboard is
|
|
207
|
+
* up, and the handlers that track the bottom and follow new content. The
|
|
208
|
+
* defaults come first so `props` can override them; the handlers come last
|
|
209
|
+
* and call the app's own after the thread's. An inverted list needs no
|
|
210
|
+
* follow: its newest message is at offset 0, so it stays in view as the
|
|
211
|
+
* content and the view change. While `paused` is true the bottom is not
|
|
212
|
+
* tracked: something other than the reader scrolls the list.
|
|
213
|
+
*/
|
|
214
|
+
function useConversationScroll({
|
|
215
|
+
onScroll,
|
|
216
|
+
onScrollBeginDrag,
|
|
217
|
+
onContentSizeChange,
|
|
218
|
+
onLayout,
|
|
219
|
+
...props
|
|
220
|
+
}, {
|
|
221
|
+
bottomInset,
|
|
222
|
+
inverted = false,
|
|
223
|
+
paused
|
|
224
|
+
} = {}) {
|
|
225
|
+
const conversation = useConversation();
|
|
226
|
+
return {
|
|
227
|
+
ref: scroller => {
|
|
228
|
+
conversation.scrollRef.current = scroller;
|
|
229
|
+
},
|
|
230
|
+
...SCROLL_DEFAULTS,
|
|
231
|
+
...props,
|
|
232
|
+
onScroll: event => {
|
|
233
|
+
if (!paused?.()) conversation.onScroll(event.nativeEvent, bottomInset?.(), inverted);
|
|
234
|
+
onScroll?.(event);
|
|
235
|
+
},
|
|
236
|
+
onScrollBeginDrag: event_0 => {
|
|
237
|
+
conversation.onUserScroll();
|
|
238
|
+
onScrollBeginDrag?.(event_0);
|
|
239
|
+
},
|
|
240
|
+
// The app's handler first, so a scroller that measures itself (`ConversationList`) has the new size to follow.
|
|
241
|
+
onContentSizeChange: (width, height) => {
|
|
242
|
+
onContentSizeChange?.(width, height);
|
|
243
|
+
if (!inverted) conversation.followContent();
|
|
244
|
+
},
|
|
245
|
+
// The keyboard or composer resizing the view keeps the end in view too.
|
|
246
|
+
onLayout: event_1 => {
|
|
247
|
+
onLayout?.(event_1);
|
|
248
|
+
if (!inverted) conversation.followContent();
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* The scrolling message column: the web `flex flex-col gap-4 px-3 py-3`. A
|
|
254
|
+
* `ScrollView`, which draws every message; a thread that can grow long
|
|
255
|
+
* should use `ConversationList`.
|
|
256
|
+
*/
|
|
257
|
+
export function ConversationContent(t0) {
|
|
258
|
+
const $ = _c(12);
|
|
259
|
+
let className;
|
|
260
|
+
let contentContainerClassName;
|
|
261
|
+
let props;
|
|
262
|
+
if ($[0] !== t0) {
|
|
263
|
+
({
|
|
264
|
+
className,
|
|
265
|
+
contentContainerClassName,
|
|
266
|
+
...props
|
|
267
|
+
} = t0);
|
|
268
|
+
$[0] = t0;
|
|
269
|
+
$[1] = className;
|
|
270
|
+
$[2] = contentContainerClassName;
|
|
271
|
+
$[3] = props;
|
|
272
|
+
} else {
|
|
273
|
+
className = $[1];
|
|
274
|
+
contentContainerClassName = $[2];
|
|
275
|
+
props = $[3];
|
|
276
|
+
}
|
|
277
|
+
let t1;
|
|
278
|
+
if ($[4] !== className) {
|
|
279
|
+
t1 = cn("flex-1", className);
|
|
280
|
+
$[4] = className;
|
|
281
|
+
$[5] = t1;
|
|
282
|
+
} else {
|
|
283
|
+
t1 = $[5];
|
|
284
|
+
}
|
|
285
|
+
let t2;
|
|
286
|
+
if ($[6] !== contentContainerClassName) {
|
|
287
|
+
t2 = cn("gap-4 px-3 py-3", contentContainerClassName);
|
|
288
|
+
$[6] = contentContainerClassName;
|
|
289
|
+
$[7] = t2;
|
|
290
|
+
} else {
|
|
291
|
+
t2 = $[7];
|
|
292
|
+
}
|
|
293
|
+
const t3 = useConversationScroll(props);
|
|
294
|
+
let t4;
|
|
295
|
+
if ($[8] !== t1 || $[9] !== t2 || $[10] !== t3) {
|
|
296
|
+
t4 = <ScrollView className={t1} contentContainerClassName={t2} {...t3} />;
|
|
297
|
+
$[8] = t1;
|
|
298
|
+
$[9] = t2;
|
|
299
|
+
$[10] = t3;
|
|
300
|
+
$[11] = t4;
|
|
301
|
+
} else {
|
|
302
|
+
t4 = $[11];
|
|
303
|
+
}
|
|
304
|
+
return t4;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* `ConversationContent` as a virtualised `FlatList`, for a thread that can
|
|
308
|
+
* grow long: only the messages near the view are drawn. The same sticking to
|
|
309
|
+
* the bottom, scroll button and keyboard behaviour. The column has the
|
|
310
|
+
* `px-3 py-3` padding but no gap, as a `FlatList` gap would also space its
|
|
311
|
+
* hidden-row placeholders: give `ItemSeparatorComponent` for the space
|
|
312
|
+
* between messages, and a stable `keyExtractor`.
|
|
313
|
+
*
|
|
314
|
+
* With `inverted` (give `data` newest first) the newest message is the first
|
|
315
|
+
* row, drawn at the bottom, so a thread opens at its end in its first frame,
|
|
316
|
+
* with no scroll and no measuring. A streamed reply (row 0) grows upwards and
|
|
317
|
+
* stays in view. Scrolled up, new content keeps what the reader sees in place
|
|
318
|
+
* (`maintainVisibleContentPosition`, iOS and Android). A thread shorter than
|
|
319
|
+
* the view starts at its top, as without `inverted`. `ListHeaderComponent`
|
|
320
|
+
* draws at the bottom, under the newest message, and `ListFooterComponent` at
|
|
321
|
+
* the top. An empty list draws `ListEmptyComponent` in place of the list, the
|
|
322
|
+
* right way up. On Android React Native flips an inverted list on both axes,
|
|
323
|
+
* so the column's left and right padding swap: keep it even, and put uneven
|
|
324
|
+
* side padding in the rows.
|
|
325
|
+
*
|
|
326
|
+
* Without `inverted`, it scrolls to the end it has measured, not with
|
|
327
|
+
* `FlatList.scrollToEnd`, which places the end from estimated row heights. A
|
|
328
|
+
* FlatList draws from its start, so a thread opened with messages would show
|
|
329
|
+
* its first rows and then jump to the end: it stays hidden until its first
|
|
330
|
+
* jump to the end (the next frame, once both sizes are known). Rows that are
|
|
331
|
+
* drawn or measured later can still move it once it shows; prefer `inverted`.
|
|
332
|
+
*
|
|
333
|
+
* With `KeyboardControllerProvider` the list keeps its size as the keyboard
|
|
334
|
+
* opens: its content gains the keyboard's height at the end and rises with
|
|
335
|
+
* the keyboard frame by frame, so the newest message stays on the composer.
|
|
336
|
+
* A drag down on it moves the keyboard with the finger once the finger
|
|
337
|
+
* reaches the composer, and lets it go at the finger's speed (iOS, Android
|
|
338
|
+
* 11+; Android 10 and below lower it at once, as without the provider).
|
|
339
|
+
*/
|
|
340
|
+
export function ConversationList({
|
|
341
|
+
className,
|
|
342
|
+
contentContainerClassName,
|
|
343
|
+
onContentSizeChange,
|
|
344
|
+
onLayout,
|
|
345
|
+
...props
|
|
346
|
+
}) {
|
|
347
|
+
const conversation = useConversation();
|
|
348
|
+
const keyboard = useKeyboardParts();
|
|
349
|
+
const insets = useSafeAreaInsets();
|
|
350
|
+
const inverted = props.inverted === true;
|
|
351
|
+
// Sizes from the list's own events, for the scroll offset; no render reads
|
|
352
|
+
// them. `inset` is the keyboard's space past the content (before offset 0
|
|
353
|
+
// when inverted); `keyboardMoving` is true while the keyboard scrolls the
|
|
354
|
+
// list, which is not the reader leaving the bottom.
|
|
355
|
+
const size = useRef({
|
|
356
|
+
content: 0,
|
|
357
|
+
view: 0,
|
|
358
|
+
inset: 0,
|
|
359
|
+
keyboardMoving: false
|
|
360
|
+
});
|
|
361
|
+
// Not inverted: hidden until the first jump to the end (see above). An
|
|
362
|
+
// empty list (a spinner, an empty state) shows at once.
|
|
363
|
+
const hasRows = (props.data?.length ?? 0) > 0;
|
|
364
|
+
const [placed, setPlaced] = useState(false);
|
|
365
|
+
const placeOnce = () => {
|
|
366
|
+
if (!inverted && !placed && hasRows && size.current.content > 0 && size.current.view > 0) {
|
|
367
|
+
requestAnimationFrame(() => setPlaced(true));
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
const wired = useConversationScroll({
|
|
371
|
+
...props,
|
|
372
|
+
onContentSizeChange: (width, height) => {
|
|
373
|
+
size.current.content = height;
|
|
374
|
+
onContentSizeChange?.(width, height);
|
|
375
|
+
placeOnce();
|
|
376
|
+
},
|
|
377
|
+
onLayout: event => {
|
|
378
|
+
size.current.view = event.nativeEvent.layout.height;
|
|
379
|
+
onLayout?.(event);
|
|
380
|
+
placeOnce();
|
|
381
|
+
}
|
|
382
|
+
}, {
|
|
383
|
+
bottomInset: () => size.current.inset,
|
|
384
|
+
inverted,
|
|
385
|
+
paused: () => size.current.keyboardMoving
|
|
386
|
+
});
|
|
387
|
+
// An inverted list would draw its empty state at the bottom, flipped back
|
|
388
|
+
// only if the element takes a `style`: draw it the right way up instead.
|
|
389
|
+
if (inverted && !hasRows) {
|
|
390
|
+
const Empty = props.ListEmptyComponent;
|
|
391
|
+
return <View style={styles.fill}>{isValidElement(Empty) ? Empty : Empty ? <Empty /> : null}</View>;
|
|
392
|
+
}
|
|
393
|
+
// The newest message's offset: inverted, 0 less the space before it.
|
|
394
|
+
const end = () => inverted ? size.current.inset > 0 ? -size.current.inset : 0 : Math.max(0, size.current.content + size.current.inset - size.current.view);
|
|
395
|
+
const list_0 = <FlatList className={cn("flex-1", className)}
|
|
396
|
+
// Inverted, the column's end is the top: a short thread sits there.
|
|
397
|
+
contentContainerClassName={cn("px-3 py-3", inverted && "grow justify-end", contentContainerClassName)} {...wired} style={[wired.style, !inverted && hasRows && !placed ? styles.hidden : null]}
|
|
398
|
+
// At the bottom, a new message shows at offset 0; scrolled up, it must not move what the reader sees.
|
|
399
|
+
maintainVisibleContentPosition={props.maintainVisibleContentPosition ?? (inverted && !conversation.isAtBottom ? KEEP_VISIBLE : undefined)} keyboardDismissMode={props.keyboardDismissMode ?? (keyboard?.interactive ? "interactive" : SCROLL_DEFAULTS.keyboardDismissMode)} renderScrollComponent={keyboard ? scrollProps => <keyboard.ChatScrollView {...scrollProps} inverted={inverted}
|
|
400
|
+
// The composer pads the bottom inset, which the keyboard covers: the content rises by the rest.
|
|
401
|
+
bottomOffset={insets.bottom} onInsetChange={inset => {
|
|
402
|
+
size.current.inset = inset;
|
|
403
|
+
}} onKeyboardMove={moving => {
|
|
404
|
+
size.current.keyboardMoving = moving;
|
|
405
|
+
}} /> : props.renderScrollComponent} ref={list => {
|
|
406
|
+
conversation.scrollRef.current = list && {
|
|
407
|
+
scrollToEnd: options => list.scrollToOffset({
|
|
408
|
+
offset: end(),
|
|
409
|
+
animated: options?.animated
|
|
410
|
+
})
|
|
411
|
+
};
|
|
412
|
+
}} />;
|
|
413
|
+
if (!keyboard) return list_0;
|
|
414
|
+
// The keyboard follows the finger from the composer's top: the dock less the inset it pads.
|
|
415
|
+
return <keyboard.GestureArea offset={Math.max(0, conversation.dockHeight - insets.bottom)} style={styles.fill}>
|
|
416
|
+
{list_0}
|
|
417
|
+
</keyboard.GestureArea>;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/** Scrolled up in an inverted list: the first drawn row in view keeps its place as rows are added or grow. */
|
|
421
|
+
const KEEP_VISIBLE = {
|
|
422
|
+
minIndexForVisible: 0
|
|
423
|
+
};
|
|
424
|
+
/** Centred placeholder for a thread with no messages: the web title (medium) and muted description. */
|
|
425
|
+
export function ConversationEmptyState(t0) {
|
|
426
|
+
const $ = _c(26);
|
|
427
|
+
let Icon;
|
|
428
|
+
let children;
|
|
429
|
+
let className;
|
|
430
|
+
let props;
|
|
431
|
+
let t1;
|
|
432
|
+
let t2;
|
|
433
|
+
if ($[0] !== t0) {
|
|
434
|
+
({
|
|
435
|
+
title: t1,
|
|
436
|
+
description: t2,
|
|
437
|
+
icon: Icon,
|
|
438
|
+
className,
|
|
439
|
+
children,
|
|
440
|
+
...props
|
|
441
|
+
} = t0);
|
|
442
|
+
$[0] = t0;
|
|
443
|
+
$[1] = Icon;
|
|
444
|
+
$[2] = children;
|
|
445
|
+
$[3] = className;
|
|
446
|
+
$[4] = props;
|
|
447
|
+
$[5] = t1;
|
|
448
|
+
$[6] = t2;
|
|
449
|
+
} else {
|
|
450
|
+
Icon = $[1];
|
|
451
|
+
children = $[2];
|
|
452
|
+
className = $[3];
|
|
453
|
+
props = $[4];
|
|
454
|
+
t1 = $[5];
|
|
455
|
+
t2 = $[6];
|
|
456
|
+
}
|
|
457
|
+
const title = t1 === undefined ? "No messages yet" : t1;
|
|
458
|
+
const description = t2 === undefined ? "Start a conversation to see messages here" : t2;
|
|
459
|
+
const scheme = useScheme();
|
|
460
|
+
const largerText = useLargerText();
|
|
461
|
+
let T0;
|
|
462
|
+
let t3;
|
|
463
|
+
let t4;
|
|
464
|
+
let t5;
|
|
465
|
+
if ($[7] !== Icon || $[8] !== children || $[9] !== className || $[10] !== description || $[11] !== largerText || $[12] !== props || $[13] !== scheme || $[14] !== title) {
|
|
466
|
+
const size = chatTextSize("small", largerText);
|
|
467
|
+
T0 = View;
|
|
468
|
+
if ($[19] !== className) {
|
|
469
|
+
t3 = cn("flex-1 items-center justify-center gap-3 p-8", className);
|
|
470
|
+
$[19] = className;
|
|
471
|
+
$[20] = t3;
|
|
472
|
+
} else {
|
|
473
|
+
t3 = $[20];
|
|
474
|
+
}
|
|
475
|
+
t4 = props;
|
|
476
|
+
t5 = children ?? <>{Icon ? <Icon size={largerText ? 28 : 24} color={tokens.color.muted[scheme]} /> : null}<View className="items-center gap-1"><Text accessibilityRole="header" className={cn(size, "text-center font-instrument-sans-medium")} style={[sizeStyle(size), textStyle("foreground", scheme), typeStyle("medium")]}>{title}</Text>{description ? <Text className={cn(size, "text-center font-instrument-sans")} style={[sizeStyle(size), textStyle("muted", scheme)]}>{description}</Text> : null}</View></>;
|
|
477
|
+
$[7] = Icon;
|
|
478
|
+
$[8] = children;
|
|
479
|
+
$[9] = className;
|
|
480
|
+
$[10] = description;
|
|
481
|
+
$[11] = largerText;
|
|
482
|
+
$[12] = props;
|
|
483
|
+
$[13] = scheme;
|
|
484
|
+
$[14] = title;
|
|
485
|
+
$[15] = T0;
|
|
486
|
+
$[16] = t3;
|
|
487
|
+
$[17] = t4;
|
|
488
|
+
$[18] = t5;
|
|
489
|
+
} else {
|
|
490
|
+
T0 = $[15];
|
|
491
|
+
t3 = $[16];
|
|
492
|
+
t4 = $[17];
|
|
493
|
+
t5 = $[18];
|
|
494
|
+
}
|
|
495
|
+
let t6;
|
|
496
|
+
if ($[21] !== T0 || $[22] !== t3 || $[23] !== t4 || $[24] !== t5) {
|
|
497
|
+
t6 = <T0 className={t3} {...t4}>{t5}</T0>;
|
|
498
|
+
$[21] = T0;
|
|
499
|
+
$[22] = t3;
|
|
500
|
+
$[23] = t4;
|
|
501
|
+
$[24] = t5;
|
|
502
|
+
$[25] = t6;
|
|
503
|
+
} else {
|
|
504
|
+
t6 = $[25];
|
|
505
|
+
}
|
|
506
|
+
return t6;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* The group under the thread, inside `Conversation` after the list: the
|
|
510
|
+
* composer, or a docked question card in its place. `ConversationScrollButton`
|
|
511
|
+
* sits centred 16 above its top edge (the web button's `bottom-4` over the
|
|
512
|
+
* dock), so the button stays with the dock as it grows (a reply quote, a
|
|
513
|
+
* taller field) and as the keyboard lifts it, instead of a fixed height over
|
|
514
|
+
* the messages.
|
|
515
|
+
*
|
|
516
|
+
* With `KeyboardControllerProvider` the dock moves with the keyboard frame by
|
|
517
|
+
* frame (`translateY`, no layout), less the bottom inset: its content pads
|
|
518
|
+
* that inset, as `PromptInput` does, and the keyboard covers it. A
|
|
519
|
+
* `PromptInput` in it then adds no lift of its own.
|
|
520
|
+
*/
|
|
521
|
+
export function ConversationDock(t0) {
|
|
522
|
+
const $ = _c(15);
|
|
523
|
+
let onLayout;
|
|
524
|
+
let props;
|
|
525
|
+
if ($[0] !== t0) {
|
|
526
|
+
({
|
|
527
|
+
onLayout,
|
|
528
|
+
...props
|
|
529
|
+
} = t0);
|
|
530
|
+
$[0] = t0;
|
|
531
|
+
$[1] = onLayout;
|
|
532
|
+
$[2] = props;
|
|
533
|
+
} else {
|
|
534
|
+
onLayout = $[1];
|
|
535
|
+
props = $[2];
|
|
536
|
+
}
|
|
537
|
+
const {
|
|
538
|
+
setDockHeight
|
|
539
|
+
} = useConversation();
|
|
540
|
+
const keyboard = useKeyboardParts();
|
|
541
|
+
const insets = useSafeAreaInsets();
|
|
542
|
+
let t1;
|
|
543
|
+
if ($[3] !== onLayout || $[4] !== setDockHeight) {
|
|
544
|
+
t1 = event => {
|
|
545
|
+
setDockHeight(event.nativeEvent.layout.height);
|
|
546
|
+
onLayout?.(event);
|
|
547
|
+
};
|
|
548
|
+
$[3] = onLayout;
|
|
549
|
+
$[4] = setDockHeight;
|
|
550
|
+
$[5] = t1;
|
|
551
|
+
} else {
|
|
552
|
+
t1 = $[5];
|
|
553
|
+
}
|
|
554
|
+
let t2;
|
|
555
|
+
if ($[6] !== props || $[7] !== t1) {
|
|
556
|
+
t2 = <View {...props} onLayout={t1} />;
|
|
557
|
+
$[6] = props;
|
|
558
|
+
$[7] = t1;
|
|
559
|
+
$[8] = t2;
|
|
560
|
+
} else {
|
|
561
|
+
t2 = $[8];
|
|
562
|
+
}
|
|
563
|
+
const dock = t2;
|
|
564
|
+
if (!keyboard) {
|
|
565
|
+
return dock;
|
|
566
|
+
}
|
|
567
|
+
let t3;
|
|
568
|
+
if ($[9] !== dock) {
|
|
569
|
+
t3 = <KeyboardLiftContext.Provider value={true}>{dock}</KeyboardLiftContext.Provider>;
|
|
570
|
+
$[9] = dock;
|
|
571
|
+
$[10] = t3;
|
|
572
|
+
} else {
|
|
573
|
+
t3 = $[10];
|
|
574
|
+
}
|
|
575
|
+
let t4;
|
|
576
|
+
if ($[11] !== insets.bottom || $[12] !== keyboard.StickyView || $[13] !== t3) {
|
|
577
|
+
t4 = <keyboard.StickyView bottomOffset={insets.bottom}>{t3}</keyboard.StickyView>;
|
|
578
|
+
$[11] = insets.bottom;
|
|
579
|
+
$[12] = keyboard.StickyView;
|
|
580
|
+
$[13] = t3;
|
|
581
|
+
$[14] = t4;
|
|
582
|
+
} else {
|
|
583
|
+
t4 = $[14];
|
|
584
|
+
}
|
|
585
|
+
return t4;
|
|
586
|
+
}
|
|
587
|
+
/** The scroll button fades in and out on the strong ease-out. */
|
|
588
|
+
const SCROLL_BUTTON_ENTER = FadeIn.duration(150).easing(EASE_OUT);
|
|
589
|
+
const SCROLL_BUTTON_EXIT = FadeOut.duration(120).easing(EASE_OUT);
|
|
590
|
+
|
|
591
|
+
/** The labelled pill's height (web `h-7`). */
|
|
592
|
+
const SCROLL_PILL_HEIGHT = 28;
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Round outline arrow, centred 16 above the bottom of the `Conversation`, or
|
|
596
|
+
* above its `ConversationDock` when it has one, shown while the reader is
|
|
597
|
+
* away from the end (the web `absolute bottom-4`).
|
|
598
|
+
* The web `Button` `icon` size (40, slop to 48). It has an opaque surface
|
|
599
|
+
* fill and no shadow, so it reads over the messages under it. With
|
|
600
|
+
* `showLabel`, a compact pill with the icon and `label`.
|
|
601
|
+
*/
|
|
602
|
+
export function ConversationScrollButton(t0) {
|
|
603
|
+
const $ = _c(22);
|
|
604
|
+
const {
|
|
605
|
+
label: t1,
|
|
606
|
+
icon: t2,
|
|
607
|
+
showLabel: t3,
|
|
608
|
+
insets,
|
|
609
|
+
testID
|
|
610
|
+
} = t0;
|
|
611
|
+
const label = t1 === undefined ? "Scroll to bottom" : t1;
|
|
612
|
+
const Icon = t2 === undefined ? IconArrowDown : t2;
|
|
613
|
+
const showLabel = t3 === undefined ? false : t3;
|
|
614
|
+
const {
|
|
615
|
+
isAtBottom,
|
|
616
|
+
scrollToBottom,
|
|
617
|
+
dockHeight
|
|
618
|
+
} = useConversation();
|
|
619
|
+
const keyboard = useKeyboardParts();
|
|
620
|
+
const safeArea = useSafeAreaInsets();
|
|
621
|
+
const scheme = useScheme();
|
|
622
|
+
const largerText = useLargerText();
|
|
623
|
+
if (isAtBottom) {
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
626
|
+
let t4;
|
|
627
|
+
if ($[0] !== largerText) {
|
|
628
|
+
t4 = chatTextSize("small", largerText);
|
|
629
|
+
$[0] = largerText;
|
|
630
|
+
$[1] = t4;
|
|
631
|
+
} else {
|
|
632
|
+
t4 = $[1];
|
|
633
|
+
}
|
|
634
|
+
const labelSize = t4;
|
|
635
|
+
const t5 = insets?.left ?? 0;
|
|
636
|
+
const t6 = insets?.right ?? 0;
|
|
637
|
+
const t7 = dockHeight + 16;
|
|
638
|
+
let t8;
|
|
639
|
+
if ($[2] !== t5 || $[3] !== t6 || $[4] !== t7) {
|
|
640
|
+
t8 = {
|
|
641
|
+
position: "absolute",
|
|
642
|
+
left: t5,
|
|
643
|
+
right: t6,
|
|
644
|
+
bottom: t7,
|
|
645
|
+
alignItems: "center"
|
|
646
|
+
};
|
|
647
|
+
$[2] = t5;
|
|
648
|
+
$[3] = t6;
|
|
649
|
+
$[4] = t7;
|
|
650
|
+
$[5] = t8;
|
|
651
|
+
} else {
|
|
652
|
+
t8 = $[5];
|
|
653
|
+
}
|
|
654
|
+
let t9;
|
|
655
|
+
if ($[6] !== Icon || $[7] !== label || $[8] !== labelSize || $[9] !== largerText || $[10] !== scheme || $[11] !== scrollToBottom || $[12] !== showLabel || $[13] !== testID) {
|
|
656
|
+
t9 = showLabel ? <Pressable accessibilityRole="button" accessibilityLabel={label} onPress={scrollToBottom} testID={testID} hitSlop={touchSlop(MIN_TOUCH_TARGET, SCROLL_PILL_HEIGHT)} style={{
|
|
657
|
+
minHeight: SCROLL_PILL_HEIGHT,
|
|
658
|
+
flexDirection: "row",
|
|
659
|
+
alignItems: "center",
|
|
660
|
+
paddingHorizontal: 12,
|
|
661
|
+
borderRadius: tokens.radius.pill,
|
|
662
|
+
borderWidth: 1,
|
|
663
|
+
borderColor: withAlpha(tokens.color.border[scheme], 0.6),
|
|
664
|
+
backgroundColor: tokens.color.surface[scheme]
|
|
665
|
+
}}>{t10 => {
|
|
666
|
+
const {
|
|
667
|
+
pressed
|
|
668
|
+
} = t10;
|
|
669
|
+
return <View style={{
|
|
670
|
+
flexDirection: "row",
|
|
671
|
+
alignItems: "center",
|
|
672
|
+
gap: 6,
|
|
673
|
+
opacity: pressed ? 0.7 : 1
|
|
674
|
+
}}><Icon size={largerText ? 20 : 16} color={tokens.color.muted[scheme]} /><Text className={cn(labelSize, "font-instrument-sans")} style={[sizeStyle(labelSize), textStyle("muted", scheme)]}>{label}</Text></View>;
|
|
675
|
+
}}</Pressable> : <Button variant="outline" size="icon" accessibilityLabel={label} onPress={scrollToBottom} testID={testID} style={{
|
|
676
|
+
borderRadius: tokens.radius.pill,
|
|
677
|
+
backgroundColor: tokens.color.surface[scheme]
|
|
678
|
+
}}><Icon size={largerText ? 20 : 16} color={tokens.color.foreground[scheme]} /></Button>;
|
|
679
|
+
$[6] = Icon;
|
|
680
|
+
$[7] = label;
|
|
681
|
+
$[8] = labelSize;
|
|
682
|
+
$[9] = largerText;
|
|
683
|
+
$[10] = scheme;
|
|
684
|
+
$[11] = scrollToBottom;
|
|
685
|
+
$[12] = showLabel;
|
|
686
|
+
$[13] = testID;
|
|
687
|
+
$[14] = t9;
|
|
688
|
+
} else {
|
|
689
|
+
t9 = $[14];
|
|
690
|
+
}
|
|
691
|
+
let t10;
|
|
692
|
+
if ($[15] !== t8 || $[16] !== t9) {
|
|
693
|
+
t10 = <NativeOnlyAnimatedView entering={SCROLL_BUTTON_ENTER} exiting={SCROLL_BUTTON_EXIT} pointerEvents="box-none" style={t8}>{t9}</NativeOnlyAnimatedView>;
|
|
694
|
+
$[15] = t8;
|
|
695
|
+
$[16] = t9;
|
|
696
|
+
$[17] = t10;
|
|
697
|
+
} else {
|
|
698
|
+
t10 = $[17];
|
|
699
|
+
}
|
|
700
|
+
const button = t10;
|
|
701
|
+
if (!keyboard) {
|
|
702
|
+
return button;
|
|
703
|
+
}
|
|
704
|
+
let t11;
|
|
705
|
+
if ($[18] !== button || $[19] !== keyboard.StickyView || $[20] !== safeArea.bottom) {
|
|
706
|
+
t11 = <keyboard.StickyView bottomOffset={safeArea.bottom} pointerEvents="box-none" style={StyleSheet.absoluteFill}>{button}</keyboard.StickyView>;
|
|
707
|
+
$[18] = button;
|
|
708
|
+
$[19] = keyboard.StickyView;
|
|
709
|
+
$[20] = safeArea.bottom;
|
|
710
|
+
$[21] = t11;
|
|
711
|
+
} else {
|
|
712
|
+
t11 = $[21];
|
|
713
|
+
}
|
|
714
|
+
return t11;
|
|
715
|
+
}
|
|
716
|
+
const styles = StyleSheet.create({
|
|
717
|
+
fill: {
|
|
718
|
+
flex: 1
|
|
719
|
+
},
|
|
720
|
+
hidden: {
|
|
721
|
+
opacity: 0
|
|
722
|
+
}
|
|
723
|
+
});
|