@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,520 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colours for `Button`, `Input`, `Textarea`, the `Select` trigger, `Switch`,
|
|
3
|
+
* `Checkbox`, `Tabs`, `Card` and component text, taken from `palette` and
|
|
4
|
+
* `tokens` instead of NativeWind theme keys (`bg-primary`, `bg-input`,
|
|
5
|
+
* `text-foreground`) that apps do not define, or define without a dark value.
|
|
6
|
+
* Also text weights and sizes as styles, the page title type, the
|
|
7
|
+
* `SearchInput` metrics, the compact window test for dialogs and the
|
|
8
|
+
* touch-target maths. No React Native import, so the tests can load this file.
|
|
9
|
+
*/
|
|
10
|
+
import { palette, withAlpha } from "./palette";
|
|
11
|
+
import { tokens } from "./tokens";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* `primary` and `default` are the same (`default` is the web name). `outline`
|
|
15
|
+
* is the web bordered neutral button. `tinted` is the pink tint that `outline`
|
|
16
|
+
* drew before 0.3.0.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Fill, border and label colour per variant, from the web classes
|
|
21
|
+
* (`bg-primary text-primary-foreground`, `bg-default text-default-foreground`
|
|
22
|
+
* and so on). `null` for `tinted`, which keeps its pink NativeWind classes.
|
|
23
|
+
*/
|
|
24
|
+
export function buttonColors(variant, scheme) {
|
|
25
|
+
const none = "transparent";
|
|
26
|
+
switch (variant) {
|
|
27
|
+
case "secondary":
|
|
28
|
+
return {
|
|
29
|
+
backgroundColor: palette.default[scheme],
|
|
30
|
+
borderColor: none,
|
|
31
|
+
color: palette.defaultForeground[scheme]
|
|
32
|
+
};
|
|
33
|
+
case "outline":
|
|
34
|
+
return {
|
|
35
|
+
backgroundColor: none,
|
|
36
|
+
borderColor: tokens.color.border[scheme],
|
|
37
|
+
color: tokens.color.foreground[scheme]
|
|
38
|
+
};
|
|
39
|
+
case "destructive":
|
|
40
|
+
return {
|
|
41
|
+
backgroundColor: palette.destructive,
|
|
42
|
+
borderColor: none,
|
|
43
|
+
color: tokens.color.white
|
|
44
|
+
};
|
|
45
|
+
case "ghost":
|
|
46
|
+
return {
|
|
47
|
+
backgroundColor: none,
|
|
48
|
+
borderColor: none,
|
|
49
|
+
color: tokens.color.muted[scheme]
|
|
50
|
+
};
|
|
51
|
+
case "link":
|
|
52
|
+
return {
|
|
53
|
+
backgroundColor: none,
|
|
54
|
+
borderColor: none,
|
|
55
|
+
color: palette.primary[scheme]
|
|
56
|
+
};
|
|
57
|
+
case "tinted":
|
|
58
|
+
return null;
|
|
59
|
+
default:
|
|
60
|
+
return {
|
|
61
|
+
backgroundColor: palette.primary[scheme],
|
|
62
|
+
borderColor: none,
|
|
63
|
+
color: palette.primaryForeground[scheme]
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The web field look (`rounded-field border border-border bg-field-background`),
|
|
70
|
+
* radius 10 as on the web. A flat object: on web the `Select` trigger is a
|
|
71
|
+
* Radix slot, which spreads `style` and so drops arrays.
|
|
72
|
+
*/
|
|
73
|
+
export function fieldStyle(scheme) {
|
|
74
|
+
return {
|
|
75
|
+
backgroundColor: tokens.color.surface[scheme],
|
|
76
|
+
borderColor: tokens.color.border[scheme],
|
|
77
|
+
borderRadius: tokens.radius.field
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Text on `fieldStyle`. From the same scheme as the fill, so the two cannot disagree. */
|
|
82
|
+
export function fieldTextStyle(scheme) {
|
|
83
|
+
return textStyle("foreground", scheme);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* `foreground` and `muted` are the web `text-foreground` and
|
|
88
|
+
* `text-muted-foreground`; `overlayMuted` is the web `DialogDescription`.
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Text colour as a style. Where an app defines the NativeWind `foreground` key
|
|
93
|
+
* with a nested `dark` shade, `text-foreground` keeps the light value in dark
|
|
94
|
+
* mode, so class-coloured text was near-black on dark surfaces.
|
|
95
|
+
*/
|
|
96
|
+
export function textStyle(tone, scheme) {
|
|
97
|
+
return {
|
|
98
|
+
color: tone === "overlayMuted" ? palette.overlayMuted[scheme] : tokens.color[tone][scheme]
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The web `font-medium` and `font-semibold`. */
|
|
103
|
+
|
|
104
|
+
const FONT_WEIGHT = {
|
|
105
|
+
medium: "500",
|
|
106
|
+
semibold: "600"
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Font weight as a style, set beside the `font-instrument-sans-*` class with
|
|
111
|
+
* the same weight. Expo web and many apps map every one of those classes to
|
|
112
|
+
* the one `Instrument Sans` family with no weight, so the class alone draws
|
|
113
|
+
* Regular. There is no `bold`: the web titles are semibold, and on Android 700
|
|
114
|
+
* can add a synthetic bold to a face that is already bold.
|
|
115
|
+
*/
|
|
116
|
+
export function typeStyle(weight) {
|
|
117
|
+
return {
|
|
118
|
+
fontWeight: FONT_WEIGHT[weight]
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Tailwind `text-*` sizes with their default line heights, in points. */
|
|
123
|
+
const TEXT_SIZE = {
|
|
124
|
+
"text-xs": {
|
|
125
|
+
fontSize: 12,
|
|
126
|
+
lineHeight: 16
|
|
127
|
+
},
|
|
128
|
+
"text-sm": {
|
|
129
|
+
fontSize: 14,
|
|
130
|
+
lineHeight: 20
|
|
131
|
+
},
|
|
132
|
+
"text-base": {
|
|
133
|
+
fontSize: 16,
|
|
134
|
+
lineHeight: 24
|
|
135
|
+
},
|
|
136
|
+
"text-lg": {
|
|
137
|
+
fontSize: 18,
|
|
138
|
+
lineHeight: 28
|
|
139
|
+
},
|
|
140
|
+
"text-xl": {
|
|
141
|
+
fontSize: 20,
|
|
142
|
+
lineHeight: 28
|
|
143
|
+
},
|
|
144
|
+
"text-2xl": {
|
|
145
|
+
fontSize: 24,
|
|
146
|
+
lineHeight: 32
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
/** Tailwind `leading-tight`, `leading-snug` and `leading-relaxed`, as multiples of the size. */
|
|
150
|
+
const LEADING = {
|
|
151
|
+
tight: 1.25,
|
|
152
|
+
snug: 1.375,
|
|
153
|
+
relaxed: 1.625
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Font size and line height of a `text-*` class as a style, set beside the
|
|
157
|
+
* class. On Expo web at 390 wide (0.3.6 gallery), `Button` labels with
|
|
158
|
+
* `text-sm` measured 16, so the class alone does not always reach the text.
|
|
159
|
+
* `"tight"`, `"snug"` and `"relaxed"` are the Tailwind `leading-tight` (1.25 ×
|
|
160
|
+
* the size), `leading-snug` (1.375 ×) and `leading-relaxed` (1.625 ×, the web
|
|
161
|
+
* `.markdown` body and the settings descriptions).
|
|
162
|
+
*/
|
|
163
|
+
export function sizeStyle(size, leading) {
|
|
164
|
+
const {
|
|
165
|
+
fontSize,
|
|
166
|
+
lineHeight
|
|
167
|
+
} = TEXT_SIZE[size];
|
|
168
|
+
return {
|
|
169
|
+
fontSize,
|
|
170
|
+
lineHeight: leading ? fontSize * LEADING[leading] : lineHeight
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Tailwind `tracking-tight` and `tracking-wide`, in em. */
|
|
175
|
+
const TRACKING = {
|
|
176
|
+
tight: -0.025,
|
|
177
|
+
wide: 0.025
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Letter spacing of a `tracking-*` class at `fontSize`, as a style in points.
|
|
182
|
+
* React Native has no `em`: `tracking-tight` (-0.025em) at 20 is -0.5.
|
|
183
|
+
*/
|
|
184
|
+
export function trackingStyle(fontSize, tracking) {
|
|
185
|
+
return {
|
|
186
|
+
letterSpacing: fontSize * TRACKING[tracking]
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* The web page title, `text-xl font-semibold leading-tight tracking-tight` on
|
|
192
|
+
* the `Breadcrumb` nav (`breadcrumb.tsx`), with the Instrument Sans semibold
|
|
193
|
+
* face for `font-semibold`. `PageTitle` sets `pageTitleStyle()` beside it.
|
|
194
|
+
*/
|
|
195
|
+
export const PAGE_TITLE_CLASS = "font-instrument-sans-semibold text-xl leading-tight tracking-tight";
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* `PAGE_TITLE_CLASS` as a style: Instrument Sans 600 at 20 / 25 with -0.5
|
|
199
|
+
* letter spacing. On Android and Expo web the face class alone can draw
|
|
200
|
+
* Regular (see `typeStyle`).
|
|
201
|
+
*/
|
|
202
|
+
export function pageTitleStyle() {
|
|
203
|
+
const size = sizeStyle("text-xl", "tight");
|
|
204
|
+
return {
|
|
205
|
+
...size,
|
|
206
|
+
...typeStyle("semibold"),
|
|
207
|
+
...trackingStyle(size.fontSize, "tight")
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Height of the `PageHeader` row below the top inset, in points. A minimum,
|
|
213
|
+
* so the title sits at the same height with or without an action, and still
|
|
214
|
+
* grows with the system font size. Native only: the web title has no row.
|
|
215
|
+
*/
|
|
216
|
+
export const PAGE_HEADER_HEIGHT = 56;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* The web `COMPACT_OVERLAY_QUERY`, `(max-width: 639px), (max-height: 500px)`:
|
|
220
|
+
* phones, and short landscape windows such as 844 × 390.
|
|
221
|
+
*/
|
|
222
|
+
export const COMPACT_OVERLAY = {
|
|
223
|
+
maxWidth: 639,
|
|
224
|
+
maxHeight: 500
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
/** Whether a window of `width` × `height` points is compact: `DialogContent` is a bottom sheet there. */
|
|
228
|
+
export function isCompactOverlay(width, height) {
|
|
229
|
+
return width <= COMPACT_OVERLAY.maxWidth || height <= COMPACT_OVERLAY.maxHeight;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* The web `Card` root (`rounded-lg bg-surface-card`): a flat fill at radius 16,
|
|
234
|
+
* with no border and no shadow. Also `SettingsSection`. A flat object, like
|
|
235
|
+
* `fieldStyle`.
|
|
236
|
+
*/
|
|
237
|
+
export function cardStyle(scheme) {
|
|
238
|
+
return {
|
|
239
|
+
backgroundColor: tokens.color.surfaceCard[scheme],
|
|
240
|
+
borderRadius: tokens.radius.lg
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** The smallest touch target on a phone, in points (Apple's 44; Material asks 48). */
|
|
245
|
+
export const MIN_TOUCH_TARGET = 44;
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* `hitSlop` that makes a control painted `width` × `height` points reach
|
|
249
|
+
* `MIN_TOUCH_TARGET` on each axis, so it can keep the web size on screen. An
|
|
250
|
+
* axis that is already large enough gets 0.
|
|
251
|
+
*/
|
|
252
|
+
export function touchSlop(width, height = width) {
|
|
253
|
+
const pad = size => Math.max(0, Math.ceil((MIN_TOUCH_TARGET - size) / 2));
|
|
254
|
+
const x = pad(width);
|
|
255
|
+
const y = pad(height);
|
|
256
|
+
return {
|
|
257
|
+
top: y,
|
|
258
|
+
bottom: y,
|
|
259
|
+
left: x,
|
|
260
|
+
right: x
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Field, icon and clear-button metrics for `SearchInput`, in points. Without
|
|
265
|
+
* larger text these are the web sizes (`search-input.tsx`, `clear-input.tsx`):
|
|
266
|
+
* `sm` is `h-8 pl-8 text-sm` with the icon at `left-2`, `md` is `h-10 pl-9`
|
|
267
|
+
* (the phone `text-base`) with the icon at `left-3`. Both have a 16 search
|
|
268
|
+
* icon, the field `px-3.5` at the end, and the `h-6 w-6` clear button at
|
|
269
|
+
* `right-2` with a 14 cross, for which a filled field reserves `pr-9`.
|
|
270
|
+
* `height` locks the field at the web height, border included (the web
|
|
271
|
+
* `h-8` and `h-10` are border-box), so the line height of the `text-*` class
|
|
272
|
+
* cannot grow it. `SearchInput` scales it with the system font size
|
|
273
|
+
* (`scaledFieldHeight`), so a large Dynamic Type size still grows the field.
|
|
274
|
+
* The padding leaves room for the line inside the lock: `sm` pads 4 above and
|
|
275
|
+
* below and `md` 7, not the web 8, because a native field adds its padding to
|
|
276
|
+
* the line height (8 drew `md` 42 high on Expo web). With larger text both
|
|
277
|
+
* sizes are the larger `Input` (64 high, `text-xl`), with a minimum height
|
|
278
|
+
* and no lock.
|
|
279
|
+
*/
|
|
280
|
+
export function searchInputSize(size, largerText) {
|
|
281
|
+
if (largerText) {
|
|
282
|
+
return {
|
|
283
|
+
minHeight: 64,
|
|
284
|
+
height: undefined,
|
|
285
|
+
paddingY: 16,
|
|
286
|
+
paddingLeft: 48,
|
|
287
|
+
paddingRight: 16,
|
|
288
|
+
filledPaddingRight: 48,
|
|
289
|
+
text: "text-xl",
|
|
290
|
+
icon: 24,
|
|
291
|
+
iconLeft: 16,
|
|
292
|
+
clear: 32,
|
|
293
|
+
clearIcon: 20,
|
|
294
|
+
clearRight: 12
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
const shared = {
|
|
298
|
+
paddingRight: 14,
|
|
299
|
+
filledPaddingRight: 36,
|
|
300
|
+
icon: 16,
|
|
301
|
+
clear: 24,
|
|
302
|
+
clearIcon: 14,
|
|
303
|
+
clearRight: 8
|
|
304
|
+
};
|
|
305
|
+
return size === "md" ? {
|
|
306
|
+
...shared,
|
|
307
|
+
minHeight: 40,
|
|
308
|
+
height: 40,
|
|
309
|
+
paddingY: 7,
|
|
310
|
+
paddingLeft: 36,
|
|
311
|
+
text: "text-base",
|
|
312
|
+
iconLeft: 12
|
|
313
|
+
} : {
|
|
314
|
+
...shared,
|
|
315
|
+
minHeight: 32,
|
|
316
|
+
height: 32,
|
|
317
|
+
paddingY: 4,
|
|
318
|
+
paddingLeft: 32,
|
|
319
|
+
text: "text-sm",
|
|
320
|
+
iconLeft: 8
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* A locked field height at the system font scale (`useWindowDimensions().fontScale`):
|
|
326
|
+
* `height` up to a scale of 1, and `height` × the scale above it, as a web
|
|
327
|
+
* `rem` height grows with the browser text size. `undefined` (no lock) stays
|
|
328
|
+
* `undefined`.
|
|
329
|
+
*/
|
|
330
|
+
export function scaledFieldHeight(height, fontScale) {
|
|
331
|
+
return height === undefined ? undefined : height * Math.max(1, fontScale);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Fill and label colour of one tab, from the web tab variables
|
|
335
|
+
* (`tabs-pills.css`). Unselected: no fill, `--muted` label. Selected: a
|
|
336
|
+
* `--default` pill or a `--segment` segment with the `--segment-foreground`
|
|
337
|
+
* label (the neutral primary, not the accent). `soft` is the web soft trigger
|
|
338
|
+
* (`bg-surface-secondary text-foreground`). Fill and label come from one
|
|
339
|
+
* scheme, so a selected label cannot match its fill.
|
|
340
|
+
*/
|
|
341
|
+
export function tabColors(variant, selected, scheme) {
|
|
342
|
+
if (!selected) return {
|
|
343
|
+
backgroundColor: "transparent",
|
|
344
|
+
color: tokens.color.muted[scheme]
|
|
345
|
+
};
|
|
346
|
+
switch (variant) {
|
|
347
|
+
case "segmented":
|
|
348
|
+
return {
|
|
349
|
+
backgroundColor: palette.segment[scheme],
|
|
350
|
+
color: palette.segmentForeground[scheme]
|
|
351
|
+
};
|
|
352
|
+
case "soft":
|
|
353
|
+
return {
|
|
354
|
+
backgroundColor: tokens.color.surfaceSecondary[scheme],
|
|
355
|
+
color: tokens.color.foreground[scheme]
|
|
356
|
+
};
|
|
357
|
+
default:
|
|
358
|
+
return {
|
|
359
|
+
backgroundColor: palette.default[scheme],
|
|
360
|
+
color: palette.segmentForeground[scheme]
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Trigger box and type for `TabsTrigger` and `PillTabs`. Phone-tuned from the
|
|
366
|
+
* web look: `pills` and `segmented` are the web `.t-tab` (`py-1`, `text-sm`)
|
|
367
|
+
* grown to 36 high with `px-3.5`, so the 4 slop above and below makes 44;
|
|
368
|
+
* `sm` is the web `sm` list (`px-2.5 py-1 text-xs`) with the label kept at 14
|
|
369
|
+
* (`text-sm`, `px-3`) and also 36 high; `soft` is the web soft trigger
|
|
370
|
+
* (`px-4 py-2 text-sm`). With larger text they are the 0.3.4 sizes.
|
|
371
|
+
* `minHeight` is a minimum, so a large Dynamic Type size grows the trigger
|
|
372
|
+
* instead of clipping its label. `letterSpacing` is the web type-scale
|
|
373
|
+
* tracking of the label (`tokens/src/css/type-scale.css`): `text-sm` is 0em.
|
|
374
|
+
*/
|
|
375
|
+
export function tabTriggerSize(variant, size, largerText) {
|
|
376
|
+
if (largerText) {
|
|
377
|
+
return size === "sm" ? {
|
|
378
|
+
minHeight: 48,
|
|
379
|
+
className: "px-4 py-2",
|
|
380
|
+
textClassName: "text-lg",
|
|
381
|
+
letterSpacing: 0
|
|
382
|
+
} : {
|
|
383
|
+
minHeight: 56,
|
|
384
|
+
className: "px-4 py-3",
|
|
385
|
+
textClassName: "text-xl",
|
|
386
|
+
letterSpacing: 0
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
if (size === "sm") return {
|
|
390
|
+
minHeight: 36,
|
|
391
|
+
className: "px-3 py-1",
|
|
392
|
+
textClassName: "text-sm",
|
|
393
|
+
letterSpacing: 0
|
|
394
|
+
};
|
|
395
|
+
return variant === "soft" ? {
|
|
396
|
+
minHeight: 36,
|
|
397
|
+
className: "px-4 py-2",
|
|
398
|
+
textClassName: "text-sm",
|
|
399
|
+
letterSpacing: 0
|
|
400
|
+
} : {
|
|
401
|
+
minHeight: 36,
|
|
402
|
+
className: "px-3.5 py-1",
|
|
403
|
+
textClassName: "text-sm",
|
|
404
|
+
letterSpacing: 0
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/** The segmented track, web `--tabs-bar-bg` (`--default`). */
|
|
409
|
+
export function tabTrackStyle(scheme) {
|
|
410
|
+
return {
|
|
411
|
+
backgroundColor: palette.default[scheme]
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Fill and label of the `FloatingTabBar` active pill. On the web the raised
|
|
417
|
+
* `--segment` pill sits on a muted `--default` track. The floating bar is
|
|
418
|
+
* already the raised surface (`tokens.color.fill`, near-white in light), so in
|
|
419
|
+
* light the pill takes the muted track fill (`--default`), as a white
|
|
420
|
+
* `--segment` pill would not show on it. In dark it keeps the kit `--segment`,
|
|
421
|
+
* lighter than the dark glass. Both are `palette.tabBarPill`. The label is the
|
|
422
|
+
* segmented `--tabs-text-active`, as `tabColors("segmented", true, scheme)`.
|
|
423
|
+
*/
|
|
424
|
+
export function floatingTabPillColors(scheme) {
|
|
425
|
+
return {
|
|
426
|
+
backgroundColor: palette.tabBarPill[scheme],
|
|
427
|
+
color: tabColors("segmented", true, scheme).color
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Box and check colours, from the web `Checkbox` classes: `bg-field-background
|
|
433
|
+
* border-border`, and `bg-primary border-primary text-primary-foreground` when
|
|
434
|
+
* checked (also when indeterminate).
|
|
435
|
+
*/
|
|
436
|
+
export function checkboxColors(checked, scheme) {
|
|
437
|
+
return checked ? {
|
|
438
|
+
backgroundColor: palette.primary[scheme],
|
|
439
|
+
borderColor: palette.primary[scheme],
|
|
440
|
+
color: palette.primaryForeground[scheme]
|
|
441
|
+
} : {
|
|
442
|
+
backgroundColor: tokens.color.surface[scheme],
|
|
443
|
+
borderColor: tokens.color.border[scheme],
|
|
444
|
+
color: palette.primaryForeground[scheme]
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Track and thumb size classes, the thumb offset off and on, and the track
|
|
449
|
+
* size in points for the hit slop. `justify-center` on the track centres the
|
|
450
|
+
* thumb. Without larger text:
|
|
451
|
+
* - `sm` is the web `Switch`: a `h-5 w-9` track (20 × 36, with a 1 border)
|
|
452
|
+
* and a `size-4` thumb that moves `translate-x-4`.
|
|
453
|
+
* - `md` is the larger app switch some web apps set through `UiConfig.Switch`:
|
|
454
|
+
* a `h-6 w-10` track (24 × 40, with a 1 border) and a `size-5` thumb that
|
|
455
|
+
* sits at `translate-x-0.5` and moves to `translate-x-[18px]`.
|
|
456
|
+
* With larger text both are the 0.3.4 size.
|
|
457
|
+
*/
|
|
458
|
+
export function switchSize(largerText, size = "sm") {
|
|
459
|
+
if (largerText) {
|
|
460
|
+
return {
|
|
461
|
+
track: "h-8 w-14",
|
|
462
|
+
thumb: "h-7 w-7",
|
|
463
|
+
off: "translate-x-0",
|
|
464
|
+
on: "translate-x-6",
|
|
465
|
+
width: 56,
|
|
466
|
+
height: 32
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
return size === "md" ? {
|
|
470
|
+
track: "h-6 w-10",
|
|
471
|
+
thumb: "h-5 w-5",
|
|
472
|
+
off: "translate-x-0.5",
|
|
473
|
+
on: "translate-x-[18px]",
|
|
474
|
+
width: 40,
|
|
475
|
+
height: 24
|
|
476
|
+
} : {
|
|
477
|
+
track: "h-5 w-9",
|
|
478
|
+
thumb: "h-4 w-4",
|
|
479
|
+
off: "translate-x-0",
|
|
480
|
+
on: "translate-x-4",
|
|
481
|
+
width: 36,
|
|
482
|
+
height: 20
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Track and thumb colours, from the web `Switch` classes. The light off thumb
|
|
488
|
+
* is white (web `bg-background`) so it shows on the grey track. Flat objects,
|
|
489
|
+
* for the same reason as `fieldStyle`.
|
|
490
|
+
*/
|
|
491
|
+
export function switchColors(checked, scheme) {
|
|
492
|
+
if (checked) {
|
|
493
|
+
return {
|
|
494
|
+
track: {
|
|
495
|
+
backgroundColor: palette.primary[scheme],
|
|
496
|
+
borderColor: palette.primary[scheme]
|
|
497
|
+
},
|
|
498
|
+
thumb: {
|
|
499
|
+
backgroundColor: palette.primaryForeground[scheme]
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
return scheme === "dark" ? {
|
|
504
|
+
track: {
|
|
505
|
+
backgroundColor: withAlpha(tokens.color.muted.dark, 0.4),
|
|
506
|
+
borderColor: withAlpha(tokens.color.muted.dark, 0.6)
|
|
507
|
+
},
|
|
508
|
+
thumb: {
|
|
509
|
+
backgroundColor: tokens.color.foreground.dark
|
|
510
|
+
}
|
|
511
|
+
} : {
|
|
512
|
+
track: {
|
|
513
|
+
backgroundColor: palette.default.light,
|
|
514
|
+
borderColor: tokens.color.border.light
|
|
515
|
+
},
|
|
516
|
+
thumb: {
|
|
517
|
+
backgroundColor: tokens.color.surface.light
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { StyleSheet } from "react-native";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* True when `style` gives a view a definite height or a flex grow. A style with
|
|
5
|
+
* only `maxHeight`, `maxWidth` or `minHeight` does not: a `flex: 1` child of
|
|
6
|
+
* such a parent collapses to height 0 on Android.
|
|
7
|
+
*/
|
|
8
|
+
export function hasDefiniteHeight(style) {
|
|
9
|
+
const flat = StyleSheet.flatten(style);
|
|
10
|
+
if (flat == null) return false;
|
|
11
|
+
return flat.height != null || flat.flex != null || flat.flexGrow != null;
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Where a `Button` sits in a dialog. The web sizes buttons in the compact
|
|
5
|
+
* dialog with descendant selectors, which React Native lacks, so `DialogContent`
|
|
6
|
+
* and `DialogFooter` pass this down instead. `compact`: inside the phone bottom
|
|
7
|
+
* sheet, where buttons are at least 44 high (web `[&_button]:min-h-11`).
|
|
8
|
+
* `stacked`: inside its footer too, where buttons fill the width (web
|
|
9
|
+
* `flex-col-reverse`).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export const DialogLayoutContext = createContext({
|
|
13
|
+
compact: false,
|
|
14
|
+
stacked: false
|
|
15
|
+
});
|
|
16
|
+
export function useDialogLayout() {
|
|
17
|
+
return useContext(DialogLayoutContext);
|
|
18
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// `@vvedantb/native-ui/auth`: `AuthProviders`, `SignInScreen`, `SignUpScreen`,
|
|
2
|
+
// `GoogleSignInButton` and the auth frame. Not on the root entry: it imports
|
|
3
|
+
// `@clerk/clerk-expo`, `convex`, `expo-auth-session`, `expo-web-browser` and
|
|
4
|
+
// `expo-router`, and Metro does not tree-shake.
|
|
5
|
+
export * from "../auth";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// `@vvedantb/native-ui/floating`: floating surfaces, glass blur targets and
|
|
2
|
+
// metrics. Reaches `expo-blur` only through its optional-peer loader.
|
|
3
|
+
export { FLOATING_INSET, FLOATING_TAB_BAR_HEIGHT, FLOATING_TAB_BAR_HEIGHT_LARGE, FLOATING_RADIUS, FLOATING_ROW_RADIUS, FLOATING_PILL_RADIUS, floatingShadow, floatingHairline, floatingFill, floatingSolidFill, tabBarReserve } from "../floating";
|
|
4
|
+
export { useReduceTransparency } from "../useReduceTransparency";
|
|
5
|
+
export { FloatingSurface, GlassSheetBackground } from "../FloatingSurface";
|
|
6
|
+
export { LiquidGlass, GlassBlurProvider, GlassBlurTarget } from "../LiquidGlass";
|
|
7
|
+
export { FloatingFAB } from "../FloatingFAB";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// `@vvedantb/native-ui/gesture-handler`. Imports `react-native-gesture-handler`
|
|
2
|
+
// (an optional peer): the only entry that does, so an app without it bundles
|
|
3
|
+
// the `PanResponder` sheets as before.
|
|
4
|
+
export { GestureHandlerProvider } from "../primitives/sheet-gesture";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// `@vvedantb/native-ui/keyboard-controller`. Imports
|
|
2
|
+
// `react-native-keyboard-controller` (an optional peer): the only entry that
|
|
3
|
+
// does, so an app without it keeps React Native's `KeyboardAvoidingView`.
|
|
4
|
+
export { KeyboardControllerProvider } from "../primitives/keyboard-controller";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// `@vvedantb/native-ui/layout`: screen containers, page headers, section
|
|
2
|
+
// labels and settings chrome.
|
|
3
|
+
export { SafeAreaContainer } from "../SafeAreaContainer";
|
|
4
|
+
export { ScreenContainer } from "../ScreenContainer";
|
|
5
|
+
export { PageTitle, PageHeader } from "../PageTitle";
|
|
6
|
+
export { StackPageHeader } from "../StackPageHeader";
|
|
7
|
+
export { SectionLabel } from "../SectionLabel";
|
|
8
|
+
export { SettingsSection } from "../settings/SettingsSection";
|
|
9
|
+
export { SettingsField } from "../settings/SettingsField";
|
|
10
|
+
export { SettingsToggleRow } from "../settings/SettingsToggleRow";
|
|
11
|
+
export { SettingsStack } from "../settings/SettingsStack";
|
|
12
|
+
export { SettingsEmptyState } from "../settings/SettingsEmptyState";
|
|
13
|
+
export { PinnedBotTile, BotPinGrid } from "../PinnedBotTile";
|
|
14
|
+
export { ArtifactList } from "../ArtifactList";
|
|
15
|
+
export { MoreFromProjectV, FromProjectV } from "../settings/MoreFromProjectV";
|
|
16
|
+
export { PROJECT_V_MOBILE_APPS, projectVMobileApp, otherProjectVMobileApps, openProjectVApp, projectVAppOpenSteps, VSTORE_HOME_URL, androidIntentUrl } from "../settings/projectVMobileApps";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// `@vvedantb/native-ui/markdown`: `Markdown`, chat markdown styled as the web
|
|
2
|
+
// `.markdown` block. Not on the root entry: it imports the optional peer
|
|
3
|
+
// `react-native-marked`, and Metro does not tree-shake.
|
|
4
|
+
export { Markdown, markdownStyles, inlineCodeStyle, MARKDOWN_SCALE, MARKDOWN_LIST } from "../markdown/Markdown";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// `@vvedantb/native-ui/page-title`: `PageTitle`, `PageHeader` and (from 0.5.0)
|
|
2
|
+
// `StackPageHeader`. Reaches no optional peer, not even a loader.
|
|
3
|
+
// `StackPageHeader` imports one Tabler icon (a required peer) for its back
|
|
4
|
+
// button; Metro does not tree-shake by default, so that bundles the Tabler
|
|
5
|
+
// package with this entry.
|
|
6
|
+
export { PageTitle, PageHeader } from "../PageTitle";
|
|
7
|
+
export { StackPageHeader } from "../StackPageHeader";
|