@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,341 @@
|
|
|
1
|
+
import { c as _c } from "react/compiler-runtime";
|
|
2
|
+
import { useContext, useRef, useState } from "react";
|
|
3
|
+
import { Pressable, StyleSheet, View } from "react-native";
|
|
4
|
+
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
|
5
|
+
import Animated, { cancelAnimation, ReduceMotion, runOnJS, useAnimatedReaction, useAnimatedStyle, useSharedValue, withSpring, withTiming } from "react-native-reanimated";
|
|
6
|
+
import { impactHaptic } from "../haptics";
|
|
7
|
+
import { InsidePagerContext } from "../Pager";
|
|
8
|
+
import { EASE_OUT } from "../motion";
|
|
9
|
+
import { palette } from "../palette";
|
|
10
|
+
import { closeSwipeRows, isSwipeRowOpen, markSwipeRowClosed, markSwipeRowOpen, SWIPE_ACTION_WIDTH, SWIPE_FAIL_OFFSET_Y, swipeRowActivation, swipeRowArmed, swipeRowOffset, swipeRowSettle } from "../swipeRow";
|
|
11
|
+
import { tokens } from "../tokens";
|
|
12
|
+
import { useScheme } from "../useScheme";
|
|
13
|
+
import { useWebClickGuard } from "./web-click-guard";
|
|
14
|
+
|
|
15
|
+
// The swipeable row on Gesture Handler: the track phase and the release run
|
|
16
|
+
// as worklets on the UI thread, so the row never lags the finger. The rules
|
|
17
|
+
// are plain worklets in `swipeRow`.
|
|
18
|
+
|
|
19
|
+
/** The release: springs on at the finger's speed, no bounce (a row overshooting rest would flash the other side). */
|
|
20
|
+
const ROW_SPRING = {
|
|
21
|
+
duration: 250,
|
|
22
|
+
dampingRatio: 1,
|
|
23
|
+
reduceMotion: ReduceMotion.System
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** The armed full swipe: the first action's icon grows a little, with the haptic, so the commit shows without it. */
|
|
27
|
+
const ARMED_SCALE = 1.15;
|
|
28
|
+
const ARMED_TIMING = {
|
|
29
|
+
duration: 150,
|
|
30
|
+
easing: EASE_OUT,
|
|
31
|
+
reduceMotion: ReduceMotion.System
|
|
32
|
+
};
|
|
33
|
+
const ICON_SIZE = 20;
|
|
34
|
+
|
|
35
|
+
/** Each tone's fill and the icon colour on it. */
|
|
36
|
+
const TONES = {
|
|
37
|
+
default: {
|
|
38
|
+
fill: palette.primary,
|
|
39
|
+
icon: palette.primaryForeground
|
|
40
|
+
},
|
|
41
|
+
warning: {
|
|
42
|
+
fill: palette.warning,
|
|
43
|
+
icon: palette.warningForeground
|
|
44
|
+
},
|
|
45
|
+
danger: {
|
|
46
|
+
fill: palette.danger,
|
|
47
|
+
icon: palette.dangerForeground
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
export function GestureSwipeableRow({
|
|
51
|
+
leadingActions = [],
|
|
52
|
+
trailingActions = [],
|
|
53
|
+
enabled = true,
|
|
54
|
+
backgroundColor,
|
|
55
|
+
children
|
|
56
|
+
}) {
|
|
57
|
+
const scheme = useScheme();
|
|
58
|
+
const x = useSharedValue(0);
|
|
59
|
+
const width = useSharedValue(0);
|
|
60
|
+
// Where the row was when the finger caught it.
|
|
61
|
+
const start = useSharedValue(0);
|
|
62
|
+
const dragging = useSharedValue(false);
|
|
63
|
+
const armed = useSharedValue(null);
|
|
64
|
+
const armedScale = useSharedValue(1);
|
|
65
|
+
// Plain values for the worklets: they must not capture the actions' functions.
|
|
66
|
+
const leading = leadingActions.length * SWIPE_ACTION_WIDTH;
|
|
67
|
+
const trailing = trailingActions.length * SWIPE_ACTION_WIDTH;
|
|
68
|
+
const leadingExits = leadingActions[0]?.exits === true;
|
|
69
|
+
const trailingExits = trailingActions[0]?.exits === true;
|
|
70
|
+
const [handle] = useState(() => ({
|
|
71
|
+
close: () => x.set(withSpring(0, ROW_SPRING))
|
|
72
|
+
}));
|
|
73
|
+
|
|
74
|
+
// Whether this touch began while a row was open, so its release only closes that row. Nothing renders from it.
|
|
75
|
+
const closesOnRelease = useRef(false);
|
|
76
|
+
// The action strips mount on the row's first touch, not with the row: a
|
|
77
|
+
// list of a hundred rows then mounts no hidden buttons and icons up front.
|
|
78
|
+
// A touch lands a few frames before a swipe can move the row, so the strip
|
|
79
|
+
// is there before any of it shows.
|
|
80
|
+
const [revealed, setRevealed] = useState(false);
|
|
81
|
+
const reveal = () => setRevealed(true);
|
|
82
|
+
const webClicks = useWebClickGuard(isSwipeRowOpen);
|
|
83
|
+
const insidePager = useContext(InsidePagerContext);
|
|
84
|
+
const opened = () => {
|
|
85
|
+
closesOnRelease.current = false;
|
|
86
|
+
webClicks.swallowNext();
|
|
87
|
+
markSwipeRowOpen(handle);
|
|
88
|
+
};
|
|
89
|
+
const closed = () => markSwipeRowClosed(handle);
|
|
90
|
+
const firstOf = side => side === "leading" ? leadingActions[0] : trailingActions[0];
|
|
91
|
+
/** Runs an action once the row slid out: if it fails, the row comes back. */
|
|
92
|
+
const runExited = action => {
|
|
93
|
+
const result = action?.onPress();
|
|
94
|
+
if (result instanceof Promise) result.catch(() => x.set(withSpring(0, ROW_SPRING)));
|
|
95
|
+
};
|
|
96
|
+
// The worklets take sides, never the actions (their functions stay on the JavaScript thread).
|
|
97
|
+
const runFirstExited = side_0 => runExited(firstOf(side_0));
|
|
98
|
+
const runFirstInPlace = side_1 => void firstOf(side_1)?.onPress();
|
|
99
|
+
/** A tapped button: the row closes, or slides out for an action that exits, then the action runs. */
|
|
100
|
+
const press = (action_0, side_2) => {
|
|
101
|
+
closed();
|
|
102
|
+
if (!action_0.exits) {
|
|
103
|
+
x.set(withSpring(0, ROW_SPRING));
|
|
104
|
+
void action_0.onPress();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const run = () => runExited(action_0);
|
|
108
|
+
x.set(withSpring(side_2 === "leading" ? width.get() : -width.get(), {
|
|
109
|
+
...ROW_SPRING,
|
|
110
|
+
overshootClamping: true
|
|
111
|
+
}, finished => {
|
|
112
|
+
"worklet";
|
|
113
|
+
|
|
114
|
+
if (finished) runOnJS(run)();
|
|
115
|
+
}));
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// The full swipe arms and disarms as the row crosses the point: the icon
|
|
119
|
+
// grows, and a light haptic fires as it arms under the finger.
|
|
120
|
+
useAnimatedReaction(() => swipeRowArmed(x.get(), {
|
|
121
|
+
leading,
|
|
122
|
+
trailing,
|
|
123
|
+
width: width.get()
|
|
124
|
+
}), (side_3, previous) => {
|
|
125
|
+
if (side_3 === previous) return;
|
|
126
|
+
armed.set(side_3);
|
|
127
|
+
armedScale.set(withTiming(side_3 ? ARMED_SCALE : 1, ARMED_TIMING));
|
|
128
|
+
if (side_3 && dragging.get()) runOnJS(impactHaptic)("light");
|
|
129
|
+
}, [leading, trailing]);
|
|
130
|
+
|
|
131
|
+
// Built on each render (for `enabled` and the actions): the detector keeps
|
|
132
|
+
// the same handler, and a drag in flight, and only takes the new config.
|
|
133
|
+
const activation = swipeRowActivation({
|
|
134
|
+
leading,
|
|
135
|
+
trailing
|
|
136
|
+
}, insidePager);
|
|
137
|
+
const pan = Gesture.Pan().enabled(enabled && leading + trailing > 0)
|
|
138
|
+
// Sideways intent first; a drag that goes vertical first is the list's scroll.
|
|
139
|
+
.activeOffsetX(activation.activeOffsetX).failOffsetY([-SWIPE_FAIL_OFFSET_Y, SWIPE_FAIL_OFFSET_Y]);
|
|
140
|
+
if (activation.failOffsetX !== undefined) pan.failOffsetX(activation.failOffsetX);
|
|
141
|
+
pan.onBegin(() => {
|
|
142
|
+
runOnJS(reveal)();
|
|
143
|
+
}).onStart(() => {
|
|
144
|
+
cancelAnimation(x);
|
|
145
|
+
start.set(x.get());
|
|
146
|
+
dragging.set(true);
|
|
147
|
+
runOnJS(opened)();
|
|
148
|
+
})
|
|
149
|
+
// Track phase: one shared value write per frame, on the UI thread.
|
|
150
|
+
.onUpdate(event => {
|
|
151
|
+
x.set(swipeRowOffset(start.get() + event.translationX, {
|
|
152
|
+
leading,
|
|
153
|
+
trailing,
|
|
154
|
+
width: width.get()
|
|
155
|
+
}));
|
|
156
|
+
}).onEnd((event_0, success) => {
|
|
157
|
+
dragging.set(false);
|
|
158
|
+
const rowWidth = width.get();
|
|
159
|
+
const settle = swipeRowSettle(x.get(), event_0.velocityX, {
|
|
160
|
+
leading,
|
|
161
|
+
trailing,
|
|
162
|
+
width: rowWidth
|
|
163
|
+
});
|
|
164
|
+
const velocity = event_0.velocityX;
|
|
165
|
+
if (success && settle.kind === "commit") {
|
|
166
|
+
// Armed by the fling rather than the position: the haptic fires now, with the commit.
|
|
167
|
+
if (armed.get() === null) runOnJS(impactHaptic)("light");
|
|
168
|
+
runOnJS(closed)();
|
|
169
|
+
const side_4 = settle.side;
|
|
170
|
+
if (side_4 === "leading" ? leadingExits : trailingExits) {
|
|
171
|
+
x.set(withSpring(side_4 === "leading" ? rowWidth : -rowWidth, {
|
|
172
|
+
...ROW_SPRING,
|
|
173
|
+
velocity,
|
|
174
|
+
overshootClamping: true
|
|
175
|
+
}, finished_0 => {
|
|
176
|
+
"worklet";
|
|
177
|
+
|
|
178
|
+
if (finished_0) runOnJS(runFirstExited)(side_4);
|
|
179
|
+
}));
|
|
180
|
+
} else {
|
|
181
|
+
x.set(withSpring(0, {
|
|
182
|
+
...ROW_SPRING,
|
|
183
|
+
velocity
|
|
184
|
+
}));
|
|
185
|
+
runOnJS(runFirstInPlace)(side_4);
|
|
186
|
+
}
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (settle.kind === "open") {
|
|
190
|
+
x.set(withSpring(settle.side === "leading" ? leading : -trailing, {
|
|
191
|
+
...ROW_SPRING,
|
|
192
|
+
velocity
|
|
193
|
+
}));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
x.set(withSpring(0, {
|
|
197
|
+
...ROW_SPRING,
|
|
198
|
+
velocity
|
|
199
|
+
}));
|
|
200
|
+
runOnJS(closed)();
|
|
201
|
+
}).onFinalize(() => {
|
|
202
|
+
dragging.set(false);
|
|
203
|
+
});
|
|
204
|
+
const rowStyle = useAnimatedStyle(() => ({
|
|
205
|
+
transform: [{
|
|
206
|
+
translateX: x.get()
|
|
207
|
+
}]
|
|
208
|
+
}));
|
|
209
|
+
return <View onLayout={event_1 => width.set(event_1.nativeEvent.layout.width)} style={styles.clip}>
|
|
210
|
+
{revealed && leadingActions.length > 0 ? <ActionStrip side="leading" actions={leadingActions} x={x} armedScale={armedScale} onPress={press} /> : null}
|
|
211
|
+
{revealed && trailingActions.length > 0 ? <ActionStrip side="trailing" actions={trailingActions} x={x} armedScale={armedScale} onPress={press} /> : null}
|
|
212
|
+
<GestureDetector gesture={pan}>
|
|
213
|
+
<Animated.View style={[{
|
|
214
|
+
backgroundColor: backgroundColor ?? tokens.color.background[scheme]
|
|
215
|
+
}, rowStyle]}>
|
|
216
|
+
<View ref={webClicks.ref}
|
|
217
|
+
// While a row is open, a tap on any row only closes it: the row
|
|
218
|
+
// takes the touch from the pressables inside.
|
|
219
|
+
onStartShouldSetResponderCapture={() => {
|
|
220
|
+
closesOnRelease.current = isSwipeRowOpen();
|
|
221
|
+
return closesOnRelease.current;
|
|
222
|
+
}} onResponderRelease={() => {
|
|
223
|
+
if (closesOnRelease.current) closeSwipeRows();
|
|
224
|
+
}}>
|
|
225
|
+
{children}
|
|
226
|
+
</View>
|
|
227
|
+
</Animated.View>
|
|
228
|
+
</GestureDetector>
|
|
229
|
+
</View>;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* One side's actions behind the row, the first at the screen edge, on the
|
|
234
|
+
* first action's fill (which stretches with a full swipe). Shown only while
|
|
235
|
+
* the row moves toward that side. Hidden from screen readers: they reach the
|
|
236
|
+
* actions on the row itself (`swipeActionsAccessibility`).
|
|
237
|
+
*/
|
|
238
|
+
function ActionStrip(t0) {
|
|
239
|
+
const $ = _c(22);
|
|
240
|
+
const {
|
|
241
|
+
side,
|
|
242
|
+
actions,
|
|
243
|
+
x,
|
|
244
|
+
armedScale,
|
|
245
|
+
onPress
|
|
246
|
+
} = t0;
|
|
247
|
+
const scheme = useScheme();
|
|
248
|
+
const reveal = actions.length * SWIPE_ACTION_WIDTH;
|
|
249
|
+
const direction = side === "leading" ? 1 : -1;
|
|
250
|
+
const stripStyle = useAnimatedStyle(() => ({
|
|
251
|
+
opacity: x.get() * direction > 0 ? 1 : 0
|
|
252
|
+
}));
|
|
253
|
+
const firstIconStyle = useAnimatedStyle(() => ({
|
|
254
|
+
transform: [{
|
|
255
|
+
translateX: direction * Math.max(0, Math.abs(x.get()) - reveal)
|
|
256
|
+
}, {
|
|
257
|
+
scale: armedScale.get()
|
|
258
|
+
}]
|
|
259
|
+
}));
|
|
260
|
+
const t1 = side === "leading" ? "row" : "row-reverse";
|
|
261
|
+
let t2;
|
|
262
|
+
if ($[0] !== t1) {
|
|
263
|
+
t2 = {
|
|
264
|
+
flexDirection: t1
|
|
265
|
+
};
|
|
266
|
+
$[0] = t1;
|
|
267
|
+
$[1] = t2;
|
|
268
|
+
} else {
|
|
269
|
+
t2 = $[1];
|
|
270
|
+
}
|
|
271
|
+
const t3 = TONES[actions[0]?.tone ?? "default"].fill[scheme];
|
|
272
|
+
let t4;
|
|
273
|
+
if ($[2] !== t3) {
|
|
274
|
+
t4 = {
|
|
275
|
+
backgroundColor: t3
|
|
276
|
+
};
|
|
277
|
+
$[2] = t3;
|
|
278
|
+
$[3] = t4;
|
|
279
|
+
} else {
|
|
280
|
+
t4 = $[3];
|
|
281
|
+
}
|
|
282
|
+
let t5;
|
|
283
|
+
if ($[4] !== stripStyle || $[5] !== t2 || $[6] !== t4) {
|
|
284
|
+
t5 = [StyleSheet.absoluteFill, t2, t4, stripStyle];
|
|
285
|
+
$[4] = stripStyle;
|
|
286
|
+
$[5] = t2;
|
|
287
|
+
$[6] = t4;
|
|
288
|
+
$[7] = t5;
|
|
289
|
+
} else {
|
|
290
|
+
t5 = $[7];
|
|
291
|
+
}
|
|
292
|
+
let t6;
|
|
293
|
+
if ($[8] !== actions || $[9] !== firstIconStyle || $[10] !== onPress || $[11] !== scheme || $[12] !== side) {
|
|
294
|
+
let t7;
|
|
295
|
+
if ($[14] !== firstIconStyle || $[15] !== onPress || $[16] !== scheme || $[17] !== side) {
|
|
296
|
+
t7 = (action, index) => {
|
|
297
|
+
const tone = TONES[action.tone ?? "default"];
|
|
298
|
+
const Icon = action.icon;
|
|
299
|
+
return <Pressable key={action.key} accessibilityRole="button" accessibilityLabel={action.label} onPress={() => onPress(action, side)} style={[styles.action, {
|
|
300
|
+
backgroundColor: tone.fill[scheme]
|
|
301
|
+
}]}>{index === 0 ? <Animated.View style={firstIconStyle}><Icon size={ICON_SIZE} color={tone.icon[scheme]} /></Animated.View> : <Icon size={ICON_SIZE} color={tone.icon[scheme]} />}</Pressable>;
|
|
302
|
+
};
|
|
303
|
+
$[14] = firstIconStyle;
|
|
304
|
+
$[15] = onPress;
|
|
305
|
+
$[16] = scheme;
|
|
306
|
+
$[17] = side;
|
|
307
|
+
$[18] = t7;
|
|
308
|
+
} else {
|
|
309
|
+
t7 = $[18];
|
|
310
|
+
}
|
|
311
|
+
t6 = actions.map(t7);
|
|
312
|
+
$[8] = actions;
|
|
313
|
+
$[9] = firstIconStyle;
|
|
314
|
+
$[10] = onPress;
|
|
315
|
+
$[11] = scheme;
|
|
316
|
+
$[12] = side;
|
|
317
|
+
$[13] = t6;
|
|
318
|
+
} else {
|
|
319
|
+
t6 = $[13];
|
|
320
|
+
}
|
|
321
|
+
let t7;
|
|
322
|
+
if ($[19] !== t5 || $[20] !== t6) {
|
|
323
|
+
t7 = <Animated.View aria-hidden={true} importantForAccessibility="no-hide-descendants" style={t5}>{t6}</Animated.View>;
|
|
324
|
+
$[19] = t5;
|
|
325
|
+
$[20] = t6;
|
|
326
|
+
$[21] = t7;
|
|
327
|
+
} else {
|
|
328
|
+
t7 = $[21];
|
|
329
|
+
}
|
|
330
|
+
return t7;
|
|
331
|
+
}
|
|
332
|
+
const styles = StyleSheet.create({
|
|
333
|
+
clip: {
|
|
334
|
+
overflow: "hidden"
|
|
335
|
+
},
|
|
336
|
+
action: {
|
|
337
|
+
width: SWIPE_ACTION_WIDTH,
|
|
338
|
+
alignItems: "center",
|
|
339
|
+
justifyContent: "center"
|
|
340
|
+
}
|
|
341
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { c as _c } from "react/compiler-runtime";
|
|
2
|
+
import * as SwitchPrimitives from "@rn-primitives/switch";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Platform } from "react-native";
|
|
5
|
+
import { cn } from "../cn";
|
|
6
|
+
const Switch = React.forwardRef((t0, ref) => {
|
|
7
|
+
const $ = _c(30);
|
|
8
|
+
let checked;
|
|
9
|
+
let className;
|
|
10
|
+
let disabled;
|
|
11
|
+
let props;
|
|
12
|
+
let style;
|
|
13
|
+
let thumbClassName;
|
|
14
|
+
let thumbStyle;
|
|
15
|
+
let trackStyle;
|
|
16
|
+
if ($[0] !== t0) {
|
|
17
|
+
({
|
|
18
|
+
className,
|
|
19
|
+
thumbClassName,
|
|
20
|
+
trackStyle,
|
|
21
|
+
thumbStyle,
|
|
22
|
+
checked,
|
|
23
|
+
style,
|
|
24
|
+
disabled,
|
|
25
|
+
...props
|
|
26
|
+
} = t0);
|
|
27
|
+
$[0] = t0;
|
|
28
|
+
$[1] = checked;
|
|
29
|
+
$[2] = className;
|
|
30
|
+
$[3] = disabled;
|
|
31
|
+
$[4] = props;
|
|
32
|
+
$[5] = style;
|
|
33
|
+
$[6] = thumbClassName;
|
|
34
|
+
$[7] = thumbStyle;
|
|
35
|
+
$[8] = trackStyle;
|
|
36
|
+
} else {
|
|
37
|
+
checked = $[1];
|
|
38
|
+
className = $[2];
|
|
39
|
+
disabled = $[3];
|
|
40
|
+
props = $[4];
|
|
41
|
+
style = $[5];
|
|
42
|
+
thumbClassName = $[6];
|
|
43
|
+
thumbStyle = $[7];
|
|
44
|
+
trackStyle = $[8];
|
|
45
|
+
}
|
|
46
|
+
const isChecked = Boolean(checked);
|
|
47
|
+
let t1;
|
|
48
|
+
if ($[9] !== style || $[10] !== trackStyle) {
|
|
49
|
+
t1 = Platform.OS === "web" || typeof style === "function" ? trackStyle : [trackStyle, style];
|
|
50
|
+
$[9] = style;
|
|
51
|
+
$[10] = trackStyle;
|
|
52
|
+
$[11] = t1;
|
|
53
|
+
} else {
|
|
54
|
+
t1 = $[11];
|
|
55
|
+
}
|
|
56
|
+
const resolvedStyle = t1;
|
|
57
|
+
let t2;
|
|
58
|
+
if ($[12] !== className || $[13] !== disabled || $[14] !== isChecked) {
|
|
59
|
+
t2 = cn("h-[1.15rem] w-8 shrink-0 rounded-full border border-transparent", Platform.select({
|
|
60
|
+
web: "outline-none transition-all"
|
|
61
|
+
}), isChecked ? "bg-primary" : "bg-input dark:bg-input/80", disabled && "opacity-50", className);
|
|
62
|
+
$[12] = className;
|
|
63
|
+
$[13] = disabled;
|
|
64
|
+
$[14] = isChecked;
|
|
65
|
+
$[15] = t2;
|
|
66
|
+
} else {
|
|
67
|
+
t2 = $[15];
|
|
68
|
+
}
|
|
69
|
+
const t3 = isChecked ? "translate-x-3.5 dark:bg-primary-foreground" : "translate-x-0 dark:bg-foreground";
|
|
70
|
+
let t4;
|
|
71
|
+
if ($[16] !== t3 || $[17] !== thumbClassName) {
|
|
72
|
+
t4 = cn("h-4 w-4 rounded-full bg-background transition-transform", t3, thumbClassName);
|
|
73
|
+
$[16] = t3;
|
|
74
|
+
$[17] = thumbClassName;
|
|
75
|
+
$[18] = t4;
|
|
76
|
+
} else {
|
|
77
|
+
t4 = $[18];
|
|
78
|
+
}
|
|
79
|
+
let t5;
|
|
80
|
+
if ($[19] !== t4 || $[20] !== thumbStyle) {
|
|
81
|
+
t5 = <SwitchPrimitives.Thumb style={thumbStyle} className={t4} />;
|
|
82
|
+
$[19] = t4;
|
|
83
|
+
$[20] = thumbStyle;
|
|
84
|
+
$[21] = t5;
|
|
85
|
+
} else {
|
|
86
|
+
t5 = $[21];
|
|
87
|
+
}
|
|
88
|
+
let t6;
|
|
89
|
+
if ($[22] !== checked || $[23] !== disabled || $[24] !== props || $[25] !== ref || $[26] !== resolvedStyle || $[27] !== t2 || $[28] !== t5) {
|
|
90
|
+
t6 = <SwitchPrimitives.Root ref={ref} checked={checked} disabled={disabled} style={resolvedStyle} className={t2} {...props}>{t5}</SwitchPrimitives.Root>;
|
|
91
|
+
$[22] = checked;
|
|
92
|
+
$[23] = disabled;
|
|
93
|
+
$[24] = props;
|
|
94
|
+
$[25] = ref;
|
|
95
|
+
$[26] = resolvedStyle;
|
|
96
|
+
$[27] = t2;
|
|
97
|
+
$[28] = t5;
|
|
98
|
+
$[29] = t6;
|
|
99
|
+
} else {
|
|
100
|
+
t6 = $[29];
|
|
101
|
+
}
|
|
102
|
+
return t6;
|
|
103
|
+
});
|
|
104
|
+
Switch.displayName = "Switch";
|
|
105
|
+
export { Switch };
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { c as _c } from "react/compiler-runtime";
|
|
2
|
+
import * as Slot from "@rn-primitives/slot";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { Platform, Text as RNText } from "react-native";
|
|
6
|
+
import { cn } from "../cn";
|
|
7
|
+
import { typeStyle } from "../controlStyles";
|
|
8
|
+
const textVariants = cva(cn("text-foreground text-base", Platform.select({
|
|
9
|
+
web: "select-text"
|
|
10
|
+
})), {
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default: "",
|
|
14
|
+
h1: cn("text-4xl font-instrument-sans-semibold tracking-tight", Platform.select({
|
|
15
|
+
web: "scroll-m-20"
|
|
16
|
+
})),
|
|
17
|
+
h2: cn("text-3xl font-instrument-sans-semibold tracking-tight border-border border-b pb-2", Platform.select({
|
|
18
|
+
web: "scroll-m-20"
|
|
19
|
+
})),
|
|
20
|
+
h3: cn("text-2xl font-instrument-sans-semibold tracking-tight", Platform.select({
|
|
21
|
+
web: "scroll-m-20"
|
|
22
|
+
})),
|
|
23
|
+
h4: cn("text-xl font-instrument-sans-semibold tracking-tight", Platform.select({
|
|
24
|
+
web: "scroll-m-20"
|
|
25
|
+
})),
|
|
26
|
+
p: "leading-7",
|
|
27
|
+
blockquote: "border-l-2 border-border pl-4 italic",
|
|
28
|
+
code: "bg-muted rounded px-[0.3rem] py-[0.2rem] text-sm font-instrument-sans-semibold",
|
|
29
|
+
lead: "text-xl text-muted-foreground",
|
|
30
|
+
large: "text-lg font-instrument-sans-semibold",
|
|
31
|
+
small: "text-sm font-instrument-sans-medium",
|
|
32
|
+
muted: "text-sm text-muted-foreground"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
defaultVariants: {
|
|
36
|
+
variant: "default"
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
const ROLE = {
|
|
40
|
+
h1: "heading",
|
|
41
|
+
h2: "heading",
|
|
42
|
+
h3: "heading",
|
|
43
|
+
h4: "heading",
|
|
44
|
+
blockquote: Platform.select({
|
|
45
|
+
web: "blockquote"
|
|
46
|
+
}),
|
|
47
|
+
code: Platform.select({
|
|
48
|
+
web: "code"
|
|
49
|
+
})
|
|
50
|
+
};
|
|
51
|
+
const ARIA_LEVEL = {
|
|
52
|
+
h1: "1",
|
|
53
|
+
h2: "2",
|
|
54
|
+
h3: "3",
|
|
55
|
+
h4: "4"
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// The weight of each variant's `font-instrument-sans-*` class, set as a style
|
|
59
|
+
// beside it (`typeStyle`): where an app maps every one of those classes to one
|
|
60
|
+
// family, the class alone draws Regular. Headings are semibold, as the web
|
|
61
|
+
// titles; `h1` was `font-extrabold` before 0.3.13.
|
|
62
|
+
const WEIGHT = {
|
|
63
|
+
h1: typeStyle("semibold"),
|
|
64
|
+
h2: typeStyle("semibold"),
|
|
65
|
+
h3: typeStyle("semibold"),
|
|
66
|
+
h4: typeStyle("semibold"),
|
|
67
|
+
code: typeStyle("semibold"),
|
|
68
|
+
large: typeStyle("semibold"),
|
|
69
|
+
small: typeStyle("medium")
|
|
70
|
+
};
|
|
71
|
+
const TextClassContext = React.createContext(undefined);
|
|
72
|
+
const Text = React.forwardRef((t0, ref) => {
|
|
73
|
+
const $ = _c(21);
|
|
74
|
+
let className;
|
|
75
|
+
let props;
|
|
76
|
+
let style;
|
|
77
|
+
let t1;
|
|
78
|
+
let t2;
|
|
79
|
+
if ($[0] !== t0) {
|
|
80
|
+
({
|
|
81
|
+
className,
|
|
82
|
+
asChild: t1,
|
|
83
|
+
variant: t2,
|
|
84
|
+
style,
|
|
85
|
+
...props
|
|
86
|
+
} = t0);
|
|
87
|
+
$[0] = t0;
|
|
88
|
+
$[1] = className;
|
|
89
|
+
$[2] = props;
|
|
90
|
+
$[3] = style;
|
|
91
|
+
$[4] = t1;
|
|
92
|
+
$[5] = t2;
|
|
93
|
+
} else {
|
|
94
|
+
className = $[1];
|
|
95
|
+
props = $[2];
|
|
96
|
+
style = $[3];
|
|
97
|
+
t1 = $[4];
|
|
98
|
+
t2 = $[5];
|
|
99
|
+
}
|
|
100
|
+
const asChild = t1 === undefined ? false : t1;
|
|
101
|
+
const variant = t2 === undefined ? "default" : t2;
|
|
102
|
+
const textClass = React.useContext(TextClassContext);
|
|
103
|
+
const Component = asChild ? Slot.Text : RNText;
|
|
104
|
+
const weight = variant ? WEIGHT[variant] : undefined;
|
|
105
|
+
let t3;
|
|
106
|
+
if ($[6] !== className || $[7] !== textClass || $[8] !== variant) {
|
|
107
|
+
t3 = cn(textVariants({
|
|
108
|
+
variant
|
|
109
|
+
}), textClass, className);
|
|
110
|
+
$[6] = className;
|
|
111
|
+
$[7] = textClass;
|
|
112
|
+
$[8] = variant;
|
|
113
|
+
$[9] = t3;
|
|
114
|
+
} else {
|
|
115
|
+
t3 = $[9];
|
|
116
|
+
}
|
|
117
|
+
const t4 = variant ? ROLE[variant] : undefined;
|
|
118
|
+
const t5 = variant ? ARIA_LEVEL[variant] : undefined;
|
|
119
|
+
let t6;
|
|
120
|
+
if ($[10] !== style || $[11] !== weight) {
|
|
121
|
+
t6 = weight ? [weight, style] : style;
|
|
122
|
+
$[10] = style;
|
|
123
|
+
$[11] = weight;
|
|
124
|
+
$[12] = t6;
|
|
125
|
+
} else {
|
|
126
|
+
t6 = $[12];
|
|
127
|
+
}
|
|
128
|
+
let t7;
|
|
129
|
+
if ($[13] !== Component || $[14] !== props || $[15] !== ref || $[16] !== t3 || $[17] !== t4 || $[18] !== t5 || $[19] !== t6) {
|
|
130
|
+
t7 = <Component ref={ref} className={t3} role={t4} aria-level={t5} style={t6} {...props} />;
|
|
131
|
+
$[13] = Component;
|
|
132
|
+
$[14] = props;
|
|
133
|
+
$[15] = ref;
|
|
134
|
+
$[16] = t3;
|
|
135
|
+
$[17] = t4;
|
|
136
|
+
$[18] = t5;
|
|
137
|
+
$[19] = t6;
|
|
138
|
+
$[20] = t7;
|
|
139
|
+
} else {
|
|
140
|
+
t7 = $[20];
|
|
141
|
+
}
|
|
142
|
+
return t7;
|
|
143
|
+
});
|
|
144
|
+
Text.displayName = "Text";
|
|
145
|
+
export { Text, TextClassContext };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { View } from "react-native";
|
|
2
|
+
import { Gesture, GestureDetector, GestureHandlerRootView } from "react-native-gesture-handler";
|
|
3
|
+
import Animated, { cancelAnimation, interpolate, ReduceMotion, runOnJS, useAnimatedStyle, useSharedValue, withSpring } from "react-native-reanimated";
|
|
4
|
+
import { impactHaptic } from "../haptics";
|
|
5
|
+
import { TOAST_DRAG_ACTIVE_OFFSET_Y, toastDragDismisses, toastDragOffset } from "../toastDrag";
|
|
6
|
+
import { useWebClickGuard } from "./web-click-guard";
|
|
7
|
+
|
|
8
|
+
// The toast drag on Gesture Handler: the track phase and the release run as
|
|
9
|
+
// worklets on the UI thread. The rules are plain worklets in `toastDrag`.
|
|
10
|
+
|
|
11
|
+
/** Back home after a short drag: a little bounce, as the finger applied force. */
|
|
12
|
+
const HOME_SPRING = {
|
|
13
|
+
duration: 250,
|
|
14
|
+
dampingRatio: 0.8,
|
|
15
|
+
reduceMotion: ReduceMotion.System
|
|
16
|
+
};
|
|
17
|
+
/** Away up the screen: no overshoot back into view. */
|
|
18
|
+
const AWAY_SPRING = {
|
|
19
|
+
duration: 250,
|
|
20
|
+
dampingRatio: 1,
|
|
21
|
+
overshootClamping: true,
|
|
22
|
+
reduceMotion: ReduceMotion.System
|
|
23
|
+
};
|
|
24
|
+
export function GestureToastDrag({
|
|
25
|
+
onDismiss,
|
|
26
|
+
onHoldChange,
|
|
27
|
+
children
|
|
28
|
+
}) {
|
|
29
|
+
const y = useSharedValue(0);
|
|
30
|
+
const height = useSharedValue(0);
|
|
31
|
+
// Where the toast was when the finger caught it, and the finger's travel then.
|
|
32
|
+
const start = useSharedValue(0);
|
|
33
|
+
const startY = useSharedValue(0);
|
|
34
|
+
const webClicks = useWebClickGuard();
|
|
35
|
+
const dismissed = () => {
|
|
36
|
+
impactHaptic("light");
|
|
37
|
+
onDismiss();
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Built on each render: the detector keeps the same handler, and a drag in flight.
|
|
41
|
+
const pan = Gesture.Pan().activeOffsetY([-TOAST_DRAG_ACTIVE_OFFSET_Y, TOAST_DRAG_ACTIVE_OFFSET_Y])
|
|
42
|
+
// The timer holds from the touch, a tap included (a tap closes the toast anyway).
|
|
43
|
+
.onBegin(() => {
|
|
44
|
+
runOnJS(onHoldChange)(true);
|
|
45
|
+
}).onStart(event => {
|
|
46
|
+
cancelAnimation(y);
|
|
47
|
+
start.set(y.get());
|
|
48
|
+
// Count from here, so the toast does not jump by the slop the drag needed to start.
|
|
49
|
+
startY.set(event.translationY);
|
|
50
|
+
runOnJS(webClicks.swallowNext)();
|
|
51
|
+
})
|
|
52
|
+
// Track phase: one shared value write per frame, on the UI thread.
|
|
53
|
+
.onUpdate(event_0 => {
|
|
54
|
+
y.set(toastDragOffset(start.get() + event_0.translationY - startY.get()));
|
|
55
|
+
}).onEnd(event_1 => {
|
|
56
|
+
const position = y.get();
|
|
57
|
+
if (toastDragDismisses(position, event_1.velocityY, height.get())) {
|
|
58
|
+
// Up past its own height, fading on the way (`toastStyle`), at the finger's speed.
|
|
59
|
+
y.set(withSpring(-height.get() * 1.5, {
|
|
60
|
+
...AWAY_SPRING,
|
|
61
|
+
velocity: event_1.velocityY
|
|
62
|
+
}, finished => {
|
|
63
|
+
"worklet";
|
|
64
|
+
|
|
65
|
+
if (finished) runOnJS(dismissed)();
|
|
66
|
+
}));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
y.set(withSpring(0, {
|
|
70
|
+
...HOME_SPRING,
|
|
71
|
+
velocity: event_1.velocityY
|
|
72
|
+
}));
|
|
73
|
+
}).onFinalize(() => {
|
|
74
|
+
runOnJS(onHoldChange)(false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Linked to the same value: the toast fades as it rises, gone one height up.
|
|
78
|
+
const toastStyle = useAnimatedStyle(() => ({
|
|
79
|
+
opacity: height.get() > 0 ? interpolate(y.get(), [-height.get(), 0], [0, 1], "clamp") : 1,
|
|
80
|
+
transform: [{
|
|
81
|
+
translateY: y.get()
|
|
82
|
+
}]
|
|
83
|
+
}));
|
|
84
|
+
return (
|
|
85
|
+
// Its own root: on iOS the toasts draw in a separate window (`FullWindowOverlay`).
|
|
86
|
+
<GestureHandlerRootView>
|
|
87
|
+
<GestureDetector gesture={pan}>
|
|
88
|
+
<Animated.View style={toastStyle} onLayout={event_2 => height.set(event_2.nativeEvent.layout.height)}>
|
|
89
|
+
<View ref={webClicks.ref}>{children}</View>
|
|
90
|
+
</Animated.View>
|
|
91
|
+
</GestureDetector>
|
|
92
|
+
</GestureHandlerRootView>
|
|
93
|
+
);
|
|
94
|
+
}
|