@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,162 @@
|
|
|
1
|
+
import { c as _c } from "react/compiler-runtime";
|
|
2
|
+
import { Pressable, Text, View } from "react-native";
|
|
3
|
+
import { sizeStyle, textStyle, typeStyle } from "../controlStyles";
|
|
4
|
+
import { tokens } from "../tokens";
|
|
5
|
+
import { useScheme } from "../useScheme";
|
|
6
|
+
/**
|
|
7
|
+
* A card for one thing a turn produced, such as a saved file or a report: an
|
|
8
|
+
* icon, a title and a muted line, on the surface fill at the surface radius
|
|
9
|
+
* (the web `Surface density="tight"`). It is at least 56 high. With `onPress`
|
|
10
|
+
* the whole card is a button that fades while pressed; otherwise `action`
|
|
11
|
+
* holds its control.
|
|
12
|
+
*/
|
|
13
|
+
export function MediaCard(t0) {
|
|
14
|
+
const $ = _c(40);
|
|
15
|
+
const {
|
|
16
|
+
icon: Icon,
|
|
17
|
+
title,
|
|
18
|
+
description,
|
|
19
|
+
onPress,
|
|
20
|
+
action,
|
|
21
|
+
trailingIcon: Trailing,
|
|
22
|
+
backgroundColor,
|
|
23
|
+
color,
|
|
24
|
+
mutedColor,
|
|
25
|
+
accessibilityLabel,
|
|
26
|
+
accessibilityHint,
|
|
27
|
+
testID
|
|
28
|
+
} = t0;
|
|
29
|
+
const scheme = useScheme();
|
|
30
|
+
const muted = mutedColor ?? tokens.color.muted[scheme];
|
|
31
|
+
let t1;
|
|
32
|
+
if ($[0] !== Icon || $[1] !== muted) {
|
|
33
|
+
t1 = <Icon size={18} color={muted} />;
|
|
34
|
+
$[0] = Icon;
|
|
35
|
+
$[1] = muted;
|
|
36
|
+
$[2] = t1;
|
|
37
|
+
} else {
|
|
38
|
+
t1 = $[2];
|
|
39
|
+
}
|
|
40
|
+
let t2;
|
|
41
|
+
let t3;
|
|
42
|
+
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
|
43
|
+
t2 = sizeStyle("text-sm");
|
|
44
|
+
t3 = typeStyle("medium");
|
|
45
|
+
$[3] = t2;
|
|
46
|
+
$[4] = t3;
|
|
47
|
+
} else {
|
|
48
|
+
t2 = $[3];
|
|
49
|
+
t3 = $[4];
|
|
50
|
+
}
|
|
51
|
+
let t4;
|
|
52
|
+
if ($[5] !== color || $[6] !== scheme) {
|
|
53
|
+
t4 = color ? {
|
|
54
|
+
color
|
|
55
|
+
} : textStyle("foreground", scheme);
|
|
56
|
+
$[5] = color;
|
|
57
|
+
$[6] = scheme;
|
|
58
|
+
$[7] = t4;
|
|
59
|
+
} else {
|
|
60
|
+
t4 = $[7];
|
|
61
|
+
}
|
|
62
|
+
let t5;
|
|
63
|
+
if ($[8] !== t4) {
|
|
64
|
+
t5 = [t2, t3, t4];
|
|
65
|
+
$[8] = t4;
|
|
66
|
+
$[9] = t5;
|
|
67
|
+
} else {
|
|
68
|
+
t5 = $[9];
|
|
69
|
+
}
|
|
70
|
+
let t6;
|
|
71
|
+
if ($[10] !== t5 || $[11] !== title) {
|
|
72
|
+
t6 = <Text numberOfLines={1} className="font-instrument-sans-medium" style={t5}>{title}</Text>;
|
|
73
|
+
$[10] = t5;
|
|
74
|
+
$[11] = title;
|
|
75
|
+
$[12] = t6;
|
|
76
|
+
} else {
|
|
77
|
+
t6 = $[12];
|
|
78
|
+
}
|
|
79
|
+
let t7;
|
|
80
|
+
if ($[13] !== description || $[14] !== muted) {
|
|
81
|
+
t7 = description ? <Text numberOfLines={1} className="font-instrument-sans" style={[sizeStyle("text-xs"), {
|
|
82
|
+
color: muted
|
|
83
|
+
}]}>{description}</Text> : null;
|
|
84
|
+
$[13] = description;
|
|
85
|
+
$[14] = muted;
|
|
86
|
+
$[15] = t7;
|
|
87
|
+
} else {
|
|
88
|
+
t7 = $[15];
|
|
89
|
+
}
|
|
90
|
+
let t8;
|
|
91
|
+
if ($[16] !== t6 || $[17] !== t7) {
|
|
92
|
+
t8 = <View className="min-w-0 flex-1">{t6}{t7}</View>;
|
|
93
|
+
$[16] = t6;
|
|
94
|
+
$[17] = t7;
|
|
95
|
+
$[18] = t8;
|
|
96
|
+
} else {
|
|
97
|
+
t8 = $[18];
|
|
98
|
+
}
|
|
99
|
+
let t9;
|
|
100
|
+
if ($[19] !== Trailing || $[20] !== muted) {
|
|
101
|
+
t9 = Trailing ? <Trailing size={16} color={muted} /> : null;
|
|
102
|
+
$[19] = Trailing;
|
|
103
|
+
$[20] = muted;
|
|
104
|
+
$[21] = t9;
|
|
105
|
+
} else {
|
|
106
|
+
t9 = $[21];
|
|
107
|
+
}
|
|
108
|
+
let t10;
|
|
109
|
+
if ($[22] !== t1 || $[23] !== t8 || $[24] !== t9) {
|
|
110
|
+
t10 = <>{t1}{t8}{t9}</>;
|
|
111
|
+
$[22] = t1;
|
|
112
|
+
$[23] = t8;
|
|
113
|
+
$[24] = t9;
|
|
114
|
+
$[25] = t10;
|
|
115
|
+
} else {
|
|
116
|
+
t10 = $[25];
|
|
117
|
+
}
|
|
118
|
+
const body = t10;
|
|
119
|
+
const t11 = backgroundColor ?? tokens.color.surface[scheme];
|
|
120
|
+
let t12;
|
|
121
|
+
if ($[26] !== t11) {
|
|
122
|
+
t12 = {
|
|
123
|
+
minHeight: 56,
|
|
124
|
+
borderRadius: tokens.radius.lg,
|
|
125
|
+
backgroundColor: t11
|
|
126
|
+
};
|
|
127
|
+
$[26] = t11;
|
|
128
|
+
$[27] = t12;
|
|
129
|
+
} else {
|
|
130
|
+
t12 = $[27];
|
|
131
|
+
}
|
|
132
|
+
const frame = t12;
|
|
133
|
+
if (onPress) {
|
|
134
|
+
const t13 = accessibilityLabel ?? title;
|
|
135
|
+
let t14;
|
|
136
|
+
if ($[28] !== accessibilityHint || $[29] !== body || $[30] !== frame || $[31] !== onPress || $[32] !== t13 || $[33] !== testID) {
|
|
137
|
+
t14 = <Pressable testID={testID} accessibilityRole="button" accessibilityLabel={t13} accessibilityHint={accessibilityHint} onPress={onPress} className="flex-row items-center gap-3 p-3 active:opacity-70" style={frame}>{body}</Pressable>;
|
|
138
|
+
$[28] = accessibilityHint;
|
|
139
|
+
$[29] = body;
|
|
140
|
+
$[30] = frame;
|
|
141
|
+
$[31] = onPress;
|
|
142
|
+
$[32] = t13;
|
|
143
|
+
$[33] = testID;
|
|
144
|
+
$[34] = t14;
|
|
145
|
+
} else {
|
|
146
|
+
t14 = $[34];
|
|
147
|
+
}
|
|
148
|
+
return t14;
|
|
149
|
+
}
|
|
150
|
+
let t13;
|
|
151
|
+
if ($[35] !== action || $[36] !== body || $[37] !== frame || $[38] !== testID) {
|
|
152
|
+
t13 = <View testID={testID} className="flex-row items-center gap-3 p-3" style={frame}>{body}{action}</View>;
|
|
153
|
+
$[35] = action;
|
|
154
|
+
$[36] = body;
|
|
155
|
+
$[37] = frame;
|
|
156
|
+
$[38] = testID;
|
|
157
|
+
$[39] = t13;
|
|
158
|
+
} else {
|
|
159
|
+
t13 = $[39];
|
|
160
|
+
}
|
|
161
|
+
return t13;
|
|
162
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { tokens } from "../tokens";
|
|
2
|
+
|
|
3
|
+
/** One image in a gallery or the full-screen viewer. */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The web `ImageGalleryPreview` metrics: the surface radius, a 2 gap between
|
|
7
|
+
* tiles (`gap-0.5`), 4:3 tiles, a lone image at most 384 high (`max-h-96`) and
|
|
8
|
+
* the hairline ring (`ring-black/10`, `dark:ring-white/10`).
|
|
9
|
+
*/
|
|
10
|
+
export const MEDIA = {
|
|
11
|
+
radius: tokens.radius.lg,
|
|
12
|
+
gap: 2,
|
|
13
|
+
tileAspect: 4 / 3,
|
|
14
|
+
singleMaxHeight: 384,
|
|
15
|
+
outline: {
|
|
16
|
+
light: "rgba(0, 0, 0, 0.1)",
|
|
17
|
+
dark: "rgba(255, 255, 255, 0.1)"
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** One tile of the grid: which image, whether it fills the left column, and the "+N" it carries. */
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The web gallery grid for `count` images: up to four tiles in two columns.
|
|
25
|
+
* With three, the first fills the left column. Past four, the fourth carries
|
|
26
|
+
* "+N" for the rest. One image is not a grid (see `ImageGallery`).
|
|
27
|
+
*/
|
|
28
|
+
export function imageGridTiles(count) {
|
|
29
|
+
const shown = Math.min(count, 4);
|
|
30
|
+
const overflow = count - shown;
|
|
31
|
+
return Array.from({
|
|
32
|
+
length: shown
|
|
33
|
+
}, (_, index) => ({
|
|
34
|
+
index,
|
|
35
|
+
spansColumn: count === 3 && index === 0,
|
|
36
|
+
overflow: index === shown - 1 ? overflow : 0
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Width over height of a lone image, from its own size once loaded, kept
|
|
42
|
+
* between 1:2 and 3:1 so a very tall or wide capture still reads. 4:3 until
|
|
43
|
+
* the size is known.
|
|
44
|
+
*/
|
|
45
|
+
export function imageAspect(size) {
|
|
46
|
+
if (!size || size.width <= 0 || size.height <= 0) return MEDIA.tileAspect;
|
|
47
|
+
return Math.min(3, Math.max(0.5, size.width / size.height));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The viewer's page for a scroll offset, kept in range. */
|
|
51
|
+
export function imageViewerPage(offsetX, pageWidth, count) {
|
|
52
|
+
if (pageWidth <= 0 || count <= 0) return 0;
|
|
53
|
+
return Math.min(count - 1, Math.max(0, Math.round(offsetX / pageWidth)));
|
|
54
|
+
}
|
package/dist/motion.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Easing, ReduceMotion } from "react-native-reanimated";
|
|
2
|
+
import { useReduceMotion } from "./useReduceMotion";
|
|
3
|
+
|
|
4
|
+
// The kit's motion values, from the `animate-expo` tables, so every part
|
|
5
|
+
// eases and springs in one family.
|
|
6
|
+
|
|
7
|
+
/** Strong ease-out: things entering or leaving the screen. */
|
|
8
|
+
export const EASE_OUT = Easing.bezier(0.23, 1, 0.32, 1);
|
|
9
|
+
|
|
10
|
+
/** A sheet opened or closed by a tap: no finger, so no velocity and no bounce. */
|
|
11
|
+
export const SHEET_SPRING = {
|
|
12
|
+
duration: 300,
|
|
13
|
+
dampingRatio: 1,
|
|
14
|
+
overshootClamping: true,
|
|
15
|
+
reduceMotion: ReduceMotion.System
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** A dragged sheet that springs home: a little bounce, as the finger applied force. Add the release `velocity`. */
|
|
19
|
+
export const SHEET_SNAP_SPRING = {
|
|
20
|
+
duration: 300,
|
|
21
|
+
dampingRatio: 0.8,
|
|
22
|
+
reduceMotion: ReduceMotion.System
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Press feedback on press-in, as a class for a `Pressable`: the 3% scale the
|
|
27
|
+
* `animate-expo` press recipe allows for something pressed this often (the
|
|
28
|
+
* web buttons and composer chips scale by 3-4%). With Reduce Motion, a dim
|
|
29
|
+
* instead of the scale.
|
|
30
|
+
*/
|
|
31
|
+
export function usePressFeedback() {
|
|
32
|
+
return useReduceMotion() ? "active:opacity-80" : "active:scale-[0.97]";
|
|
33
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rules of a swiped `Pager`, as plain numbers, so they stay testable
|
|
3
|
+
* apart from the gesture. The pager's position is in pages: 0 is the first
|
|
4
|
+
* page, 1.5 is halfway between the second and the third. Distances are in
|
|
5
|
+
* points and velocities in points per second (the Gesture Handler units),
|
|
6
|
+
* positive to the right. Each rule is a worklet, so the drag runs it on the
|
|
7
|
+
* UI thread.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Sideways travel (pt) before a drag takes the pager: twice a row swipe's
|
|
12
|
+
* (`SWIPE_ACTIVE_OFFSET_X`), so a swipeable row under the finger takes its
|
|
13
|
+
* own swipe first, and only a clearly sideways drag moves the pages.
|
|
14
|
+
*/
|
|
15
|
+
export const PAGER_ACTIVE_OFFSET_X = 24;
|
|
16
|
+
|
|
17
|
+
/** Vertical travel (pt) that leaves the touch to the page's own scroll. */
|
|
18
|
+
export const PAGER_FAIL_OFFSET_Y = 14;
|
|
19
|
+
|
|
20
|
+
/** A sideways flick at this speed (pt/s) turns the page, however short the drag. */
|
|
21
|
+
const FLICK_VELOCITY = 500;
|
|
22
|
+
|
|
23
|
+
/** How far ahead (s) the release velocity carries the pages when deciding where they settle. */
|
|
24
|
+
const PROJECTION_S = 0.2;
|
|
25
|
+
|
|
26
|
+
/** How far the pages move past the first or last page: less and less the further the finger goes. */
|
|
27
|
+
function rubberband(overshoot, dimension) {
|
|
28
|
+
"worklet";
|
|
29
|
+
|
|
30
|
+
const constant = 0.55;
|
|
31
|
+
return overshoot * dimension * constant / (dimension + constant * overshoot);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Where the pages sit for a finger `dx` points from where the drag caught
|
|
36
|
+
* them at `start`: they follow the finger 1:1 (a drag left shows the next
|
|
37
|
+
* page), and past the first or last page they meet growing resistance.
|
|
38
|
+
*/
|
|
39
|
+
export function pagerPosition(start, dx, width, count) {
|
|
40
|
+
"worklet";
|
|
41
|
+
|
|
42
|
+
if (width <= 0) return start;
|
|
43
|
+
const raw = start - dx / width;
|
|
44
|
+
const last = count - 1;
|
|
45
|
+
if (raw < 0) return -rubberband(-raw * width, width) / width;
|
|
46
|
+
if (raw > last) return last + rubberband((raw - last) * width, width) / width;
|
|
47
|
+
return raw;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The page a released drag settles on, from the page it started on
|
|
52
|
+
* (`from`), where it is (`position`) and the finger's speed (`vx`): a flick
|
|
53
|
+
* past `FLICK_VELOCITY` the way it moved turns one page; else the position
|
|
54
|
+
* carried on by its speed rounds to the nearest page. Never more than one
|
|
55
|
+
* page from `from`, and never past the ends.
|
|
56
|
+
*/
|
|
57
|
+
export function pagerSettle(position, vx, width, from, count) {
|
|
58
|
+
"worklet";
|
|
59
|
+
|
|
60
|
+
const moved = position - from;
|
|
61
|
+
// A drag left (negative `vx`) moves toward the next page (a larger position).
|
|
62
|
+
const toward = -Math.sign(vx);
|
|
63
|
+
let target;
|
|
64
|
+
if (Math.abs(vx) >= FLICK_VELOCITY && moved !== 0 && toward === Math.sign(moved)) {
|
|
65
|
+
target = from + toward;
|
|
66
|
+
} else {
|
|
67
|
+
target = Math.round(width > 0 ? position - vx * PROJECTION_S / width : position);
|
|
68
|
+
}
|
|
69
|
+
return Math.min(count - 1, Math.max(0, Math.min(from + 1, Math.max(from - 1, target))));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Every page from `from` to `to`, both included: the pages a slide from
|
|
74
|
+
* outside (a tab press) passes, which the pager draws so it never crosses a
|
|
75
|
+
* blank page.
|
|
76
|
+
*/
|
|
77
|
+
export function pagesBetween(from, to) {
|
|
78
|
+
const low = Math.min(from, to);
|
|
79
|
+
return Array.from({
|
|
80
|
+
length: Math.abs(to - from) + 1
|
|
81
|
+
}, (_, step) => low + step);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The scroll offset (pt) of a sideways tab row that centres the tab at
|
|
86
|
+
* `position` (in pages, as the pager's: 1.5 is halfway between the second
|
|
87
|
+
* and the third tab), given each tab's `x` and `width` in the row and the
|
|
88
|
+
* row's visible `viewport` width. Between two tabs it blends their centres;
|
|
89
|
+
* it never scrolls before the first tab or past the last.
|
|
90
|
+
*/
|
|
91
|
+
export function centredTabScroll(tabs, position, viewport) {
|
|
92
|
+
"worklet";
|
|
93
|
+
|
|
94
|
+
const last = tabs[tabs.length - 1];
|
|
95
|
+
if (!last || viewport <= 0) return 0;
|
|
96
|
+
const at = Math.min(tabs.length - 1, Math.max(0, position));
|
|
97
|
+
const below = Math.floor(at);
|
|
98
|
+
const left = tabs[below] ?? last;
|
|
99
|
+
const right = tabs[Math.min(tabs.length - 1, below + 1)] ?? last;
|
|
100
|
+
const share = at - below;
|
|
101
|
+
const centre = left.x + left.width / 2 + (right.x + right.width / 2 - (left.x + left.width / 2)) * share;
|
|
102
|
+
const end = Math.max(0, last.x + last.width - viewport);
|
|
103
|
+
return Math.min(end, Math.max(0, centre - viewport / 2));
|
|
104
|
+
}
|
package/dist/palette.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web semantic colours for React Native, converted from `@vvedantb/ui`
|
|
3
|
+
* `tokens.css` (oklch → sRGB hex, clipped to sRGB). `tokens.ts` holds the
|
|
4
|
+
* neutrals the native app ships; these are the colours it lacks (primary,
|
|
5
|
+
* danger, success, warning, overlay, segment, tab bar pill, field placeholder, backdrop,
|
|
6
|
+
* dialog description).
|
|
7
|
+
*
|
|
8
|
+
* A test in this package converts `tokens.css` and fails when a value here
|
|
9
|
+
* drifts, so change `tokens.css` first and then this file.
|
|
10
|
+
*/
|
|
11
|
+
export const palette = {
|
|
12
|
+
/** `--accent` (web `primary`): near-black in light, near-white in dark. */
|
|
13
|
+
primary: {
|
|
14
|
+
light: "#181818",
|
|
15
|
+
dark: "#fcfcfc"
|
|
16
|
+
},
|
|
17
|
+
primaryForeground: {
|
|
18
|
+
light: "#fcfcfc",
|
|
19
|
+
dark: "#181818"
|
|
20
|
+
},
|
|
21
|
+
danger: {
|
|
22
|
+
light: "#ff373c",
|
|
23
|
+
dark: "#db3b3e"
|
|
24
|
+
},
|
|
25
|
+
dangerForeground: {
|
|
26
|
+
light: "#fcfcfc",
|
|
27
|
+
dark: "#fcfcfc"
|
|
28
|
+
},
|
|
29
|
+
success: {
|
|
30
|
+
light: "#15c964",
|
|
31
|
+
dark: "#15c964"
|
|
32
|
+
},
|
|
33
|
+
successForeground: {
|
|
34
|
+
light: "#161917",
|
|
35
|
+
dark: "#161917"
|
|
36
|
+
},
|
|
37
|
+
warning: {
|
|
38
|
+
light: "#f5a523",
|
|
39
|
+
dark: "#f7b750"
|
|
40
|
+
},
|
|
41
|
+
warningForeground: {
|
|
42
|
+
light: "#1a1815",
|
|
43
|
+
dark: "#1a1815"
|
|
44
|
+
},
|
|
45
|
+
/** `--default` (web `secondary`): neutral control fill. */
|
|
46
|
+
default: {
|
|
47
|
+
light: "#ebebeb",
|
|
48
|
+
dark: "#272727"
|
|
49
|
+
},
|
|
50
|
+
defaultForeground: {
|
|
51
|
+
light: "#181818",
|
|
52
|
+
dark: "#fcfcfc"
|
|
53
|
+
},
|
|
54
|
+
overlay: {
|
|
55
|
+
light: "#ffffff",
|
|
56
|
+
dark: "#181818"
|
|
57
|
+
},
|
|
58
|
+
overlayForeground: {
|
|
59
|
+
light: "#181818",
|
|
60
|
+
dark: "#fcfcfc"
|
|
61
|
+
},
|
|
62
|
+
segment: {
|
|
63
|
+
light: "#ffffff",
|
|
64
|
+
dark: "#474747"
|
|
65
|
+
},
|
|
66
|
+
segmentForeground: {
|
|
67
|
+
light: "#181818",
|
|
68
|
+
dark: "#fcfcfc"
|
|
69
|
+
},
|
|
70
|
+
/**
|
|
71
|
+
* The `FloatingTabBar` active pill: `--default` in light (it shows on the
|
|
72
|
+
* near-white glass bar) and `--segment` in dark. Its own key, so an app that
|
|
73
|
+
* themes `segment` for the segmented `Tabs` thumb keeps a pill that shows on
|
|
74
|
+
* the dark glass bar.
|
|
75
|
+
*/
|
|
76
|
+
tabBarPill: {
|
|
77
|
+
light: "#ebebeb",
|
|
78
|
+
dark: "#474747"
|
|
79
|
+
},
|
|
80
|
+
surfaceTertiary: {
|
|
81
|
+
light: "#eaeaea",
|
|
82
|
+
dark: "#282627"
|
|
83
|
+
},
|
|
84
|
+
fieldPlaceholder: {
|
|
85
|
+
light: "#727272",
|
|
86
|
+
dark: "#a0a0a0"
|
|
87
|
+
},
|
|
88
|
+
backdrop: {
|
|
89
|
+
light: "rgba(0, 0, 0, 0.5)",
|
|
90
|
+
dark: "rgba(0, 0, 0, 0.6)"
|
|
91
|
+
},
|
|
92
|
+
/** Web `DialogDescription` text: `--overlay-foreground` mixed 72% into `--overlay` (oklch), for 4.5:1 on the overlay. */
|
|
93
|
+
overlayMuted: {
|
|
94
|
+
light: "#505050",
|
|
95
|
+
dark: "#b5b5b5"
|
|
96
|
+
},
|
|
97
|
+
/** Fill of the web destructive `Button` (`oklch(47.2% 0.195 25.7)`), the same in both schemes. */
|
|
98
|
+
destructive: "#af0015"
|
|
99
|
+
};
|
|
100
|
+
/** `#rrggbb` with an alpha, as `rgba(...)`. For tints such as the badge fills (web `bg-danger/12`). */
|
|
101
|
+
export function withAlpha(hex, alpha) {
|
|
102
|
+
const n = Number.parseInt(hex.slice(1, 7), 16);
|
|
103
|
+
return `rgba(${n >> 16 & 255}, ${n >> 8 & 255}, ${n & 255}, ${alpha})`;
|
|
104
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { c as _c } from "react/compiler-runtime";
|
|
2
|
+
import { TextClassContext } from "./text";
|
|
3
|
+
import { cn } from "../cn";
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import { Platform, Pressable } from "react-native";
|
|
7
|
+
import { usePressFeedback } from "../motion";
|
|
8
|
+
const buttonVariants = cva(cn("group flex-row items-center justify-center gap-2 shadow-none", Platform.select({
|
|
9
|
+
web: "outline-none transition-all"
|
|
10
|
+
})), {
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default: cn("bg-primary", Platform.select({
|
|
14
|
+
web: "hover:bg-primary/90"
|
|
15
|
+
})),
|
|
16
|
+
destructive: cn("bg-destructive", Platform.select({
|
|
17
|
+
web: "hover:bg-destructive/90"
|
|
18
|
+
})),
|
|
19
|
+
outline: cn("border border-border bg-background", Platform.select({
|
|
20
|
+
web: "hover:bg-accent"
|
|
21
|
+
})),
|
|
22
|
+
secondary: cn("bg-secondary", Platform.select({
|
|
23
|
+
web: "hover:bg-secondary/80"
|
|
24
|
+
})),
|
|
25
|
+
ghost: cn(Platform.select({
|
|
26
|
+
web: "hover:bg-accent"
|
|
27
|
+
})),
|
|
28
|
+
link: ""
|
|
29
|
+
},
|
|
30
|
+
size: {
|
|
31
|
+
default: "h-10 px-4 py-2",
|
|
32
|
+
sm: "h-8 px-3",
|
|
33
|
+
lg: "h-11 px-6",
|
|
34
|
+
icon: "h-10 w-10"
|
|
35
|
+
},
|
|
36
|
+
// The web `shape`: `pill` (default) is fully round, `rounded` the radius
|
|
37
|
+
// before 0.6.0. `Button` also sets the radius as a style, which wins.
|
|
38
|
+
shape: {
|
|
39
|
+
pill: "rounded-full",
|
|
40
|
+
rounded: "rounded-lg"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
defaultVariants: {
|
|
44
|
+
variant: "default",
|
|
45
|
+
size: "default",
|
|
46
|
+
shape: "pill"
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// No weight: a class context cannot carry the `typeStyle` that a face class
|
|
51
|
+
// needs, so `Button` sets the medium face and its weight on its label.
|
|
52
|
+
const buttonTextVariants = cva("text-sm text-foreground", {
|
|
53
|
+
variants: {
|
|
54
|
+
variant: {
|
|
55
|
+
default: "text-primary-foreground",
|
|
56
|
+
destructive: "text-white",
|
|
57
|
+
outline: "text-accent-foreground",
|
|
58
|
+
secondary: "text-secondary-foreground",
|
|
59
|
+
ghost: "text-accent-foreground",
|
|
60
|
+
link: "text-primary underline"
|
|
61
|
+
},
|
|
62
|
+
size: {
|
|
63
|
+
default: "",
|
|
64
|
+
sm: "",
|
|
65
|
+
lg: "",
|
|
66
|
+
icon: ""
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
defaultVariants: {
|
|
70
|
+
variant: "default",
|
|
71
|
+
size: "default"
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
const Button = React.forwardRef((t0, ref) => {
|
|
75
|
+
const $ = _c(12);
|
|
76
|
+
const {
|
|
77
|
+
className,
|
|
78
|
+
variant,
|
|
79
|
+
size,
|
|
80
|
+
shape,
|
|
81
|
+
...props
|
|
82
|
+
} = t0;
|
|
83
|
+
const pressed = usePressFeedback();
|
|
84
|
+
const t1 = TextClassContext;
|
|
85
|
+
let t2;
|
|
86
|
+
if ($[0] !== size || $[1] !== variant) {
|
|
87
|
+
t2 = buttonTextVariants({
|
|
88
|
+
variant,
|
|
89
|
+
size
|
|
90
|
+
});
|
|
91
|
+
$[0] = size;
|
|
92
|
+
$[1] = variant;
|
|
93
|
+
$[2] = t2;
|
|
94
|
+
} else {
|
|
95
|
+
t2 = $[2];
|
|
96
|
+
}
|
|
97
|
+
const T0 = Pressable;
|
|
98
|
+
const t3 = cn(buttonVariants({
|
|
99
|
+
variant,
|
|
100
|
+
size,
|
|
101
|
+
shape
|
|
102
|
+
}), pressed, props.disabled && "opacity-45", className);
|
|
103
|
+
let t4;
|
|
104
|
+
if ($[3] !== T0 || $[4] !== props || $[5] !== ref || $[6] !== t3) {
|
|
105
|
+
t4 = <T0 ref={ref} className={t3} role="button" {...props} />;
|
|
106
|
+
$[3] = T0;
|
|
107
|
+
$[4] = props;
|
|
108
|
+
$[5] = ref;
|
|
109
|
+
$[6] = t3;
|
|
110
|
+
$[7] = t4;
|
|
111
|
+
} else {
|
|
112
|
+
t4 = $[7];
|
|
113
|
+
}
|
|
114
|
+
let t5;
|
|
115
|
+
if ($[8] !== t1.Provider || $[9] !== t2 || $[10] !== t4) {
|
|
116
|
+
t5 = <t1.Provider value={t2}>{t4}</t1.Provider>;
|
|
117
|
+
$[8] = t1.Provider;
|
|
118
|
+
$[9] = t2;
|
|
119
|
+
$[10] = t4;
|
|
120
|
+
$[11] = t5;
|
|
121
|
+
} else {
|
|
122
|
+
t5 = $[11];
|
|
123
|
+
}
|
|
124
|
+
return t5;
|
|
125
|
+
});
|
|
126
|
+
Button.displayName = "Button";
|
|
127
|
+
export { Button, buttonTextVariants, buttonVariants };
|