@vvedantb/native-ui 0.2.1 → 0.3.13

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.
Files changed (91) hide show
  1. package/README.md +171 -9
  2. package/package.json +84 -2
  3. package/src/AlertDialog.tsx +73 -0
  4. package/src/AmbientBackground.tsx +26 -11
  5. package/src/Avatar.tsx +141 -0
  6. package/src/Badge.tsx +108 -0
  7. package/src/BottomSheetContainer.tsx +7 -1
  8. package/src/Button.tsx +153 -52
  9. package/src/Card.tsx +111 -36
  10. package/src/Checkbox.tsx +78 -0
  11. package/src/Chip.tsx +5 -3
  12. package/src/ConfirmDialog.tsx +105 -0
  13. package/src/DateTimeField.tsx +6 -2
  14. package/src/Dialog.tsx +253 -0
  15. package/src/EmptyState.tsx +122 -7
  16. package/src/FloatingFAB.tsx +2 -4
  17. package/src/FloatingSurface.tsx +31 -9
  18. package/src/FloatingTabBar.tsx +39 -11
  19. package/src/Input.tsx +15 -4
  20. package/src/Label.tsx +31 -0
  21. package/src/LabeledSwitchRow.tsx +96 -0
  22. package/src/LiquidGlass.tsx +2 -10
  23. package/src/ListRow.tsx +7 -2
  24. package/src/PageTitle.tsx +81 -0
  25. package/src/PillTabs.tsx +28 -14
  26. package/src/Progress.tsx +67 -0
  27. package/src/SearchInput.tsx +163 -0
  28. package/src/Select.tsx +32 -6
  29. package/src/Separator.tsx +38 -0
  30. package/src/Sheet.tsx +107 -0
  31. package/src/Skeleton.tsx +25 -5
  32. package/src/Spinner.tsx +92 -0
  33. package/src/StatusDot.tsx +66 -0
  34. package/src/Switch.tsx +24 -7
  35. package/src/Tabs.tsx +190 -0
  36. package/src/Textarea.tsx +47 -0
  37. package/src/Toast.tsx +170 -0
  38. package/src/blur.ts +48 -0
  39. package/src/brand/AppIcons.tsx +106 -0
  40. package/src/brand/AppMark.tsx +83 -0
  41. package/src/brand/AppWordmark.tsx +97 -0
  42. package/src/brand/familyApps.ts +24 -0
  43. package/src/brand/index.ts +34 -0
  44. package/src/brand/svg.ts +263 -0
  45. package/src/brand/vProAppBenefits.ts +22 -0
  46. package/src/controlStyles.ts +395 -0
  47. package/src/dialogLayout.ts +20 -0
  48. package/src/entries/ambient.ts +3 -0
  49. package/src/entries/bottom-sheet.ts +2 -0
  50. package/src/entries/brand.ts +2 -0
  51. package/src/entries/button.ts +3 -0
  52. package/src/entries/date-time.ts +3 -0
  53. package/src/entries/floating.ts +24 -0
  54. package/src/entries/layout.ts +10 -0
  55. package/src/entries/marketing.ts +10 -0
  56. package/src/entries/page-title.ts +4 -0
  57. package/src/entries/primitives.ts +99 -0
  58. package/src/entries/search-input.ts +4 -0
  59. package/src/entries/tab-bar.ts +10 -0
  60. package/src/entries/theme.ts +15 -0
  61. package/src/entries/toast.ts +6 -0
  62. package/src/examples/ParityGallery.tsx +513 -0
  63. package/src/floating.ts +5 -1
  64. package/src/glassBlur.tsx +95 -0
  65. package/src/gradient.ts +21 -0
  66. package/src/haptics.ts +22 -2
  67. package/src/index.ts +101 -2
  68. package/src/initials.ts +10 -0
  69. package/src/marketing/PricingModal.tsx +410 -0
  70. package/src/marketing/UpgradeToProCard.tsx +103 -0
  71. package/src/marketing/pricing.ts +76 -0
  72. package/src/palette.ts +43 -0
  73. package/src/primitives/button.tsx +3 -1
  74. package/src/primitives/card.tsx +5 -2
  75. package/src/primitives/input.tsx +9 -2
  76. package/src/primitives/native-only-animated-view.tsx +10 -3
  77. package/src/primitives/overlay.tsx +179 -0
  78. package/src/primitives/select.tsx +25 -12
  79. package/src/primitives/switch.tsx +12 -13
  80. package/src/primitives/text.tsx +25 -8
  81. package/src/primitives/use-controllable-state.ts +24 -0
  82. package/src/settings/SettingsField.tsx +11 -2
  83. package/src/settings/SettingsSection.tsx +20 -6
  84. package/src/settings/SettingsToggleRow.tsx +7 -2
  85. package/src/tabBarRoutes.ts +52 -0
  86. package/src/toastChrome.ts +103 -0
  87. package/src/toastStore.ts +86 -0
  88. package/src/tokens.ts +19 -14
  89. package/src/typography.ts +24 -0
  90. package/src/useReduceMotion.ts +24 -0
  91. package/src/useScheme.ts +6 -0
@@ -0,0 +1,395 @@
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
+ type Scheme = "light" | "dark";
14
+
15
+ /**
16
+ * `primary` and `default` are the same (`default` is the web name). `outline`
17
+ * is the web bordered neutral button. `tinted` is the pink tint that `outline`
18
+ * drew before 0.3.0.
19
+ */
20
+ export type ButtonVariant =
21
+ | "primary"
22
+ | "default"
23
+ | "secondary"
24
+ | "outline"
25
+ | "destructive"
26
+ | "ghost"
27
+ | "link"
28
+ | "tinted";
29
+
30
+ export type ButtonColors = {
31
+ backgroundColor: string;
32
+ borderColor: string;
33
+ color: string;
34
+ };
35
+
36
+ /**
37
+ * Fill, border and label colour per variant, from the web classes
38
+ * (`bg-primary text-primary-foreground`, `bg-default text-default-foreground`
39
+ * and so on). `null` for `tinted`, which keeps its pink NativeWind classes.
40
+ */
41
+ export function buttonColors(variant: ButtonVariant, scheme: Scheme): ButtonColors | null {
42
+ const none = "transparent";
43
+ switch (variant) {
44
+ case "secondary":
45
+ return {
46
+ backgroundColor: palette.default[scheme],
47
+ borderColor: none,
48
+ color: palette.defaultForeground[scheme],
49
+ };
50
+ case "outline":
51
+ return {
52
+ backgroundColor: none,
53
+ borderColor: tokens.color.border[scheme],
54
+ color: tokens.color.foreground[scheme],
55
+ };
56
+ case "destructive":
57
+ return { backgroundColor: palette.destructive, borderColor: none, color: tokens.color.white };
58
+ case "ghost":
59
+ return { backgroundColor: none, borderColor: none, color: tokens.color.muted[scheme] };
60
+ case "link":
61
+ return { backgroundColor: none, borderColor: none, color: palette.primary[scheme] };
62
+ case "tinted":
63
+ return null;
64
+ default:
65
+ return {
66
+ backgroundColor: palette.primary[scheme],
67
+ borderColor: none,
68
+ color: palette.primaryForeground[scheme],
69
+ };
70
+ }
71
+ }
72
+
73
+ /**
74
+ * The web field look (`rounded-field border border-border bg-field-background`),
75
+ * radius 10 as on the web. A flat object: on web the `Select` trigger is a
76
+ * Radix slot, which spreads `style` and so drops arrays.
77
+ */
78
+ export function fieldStyle(scheme: Scheme) {
79
+ return {
80
+ backgroundColor: tokens.color.surface[scheme],
81
+ borderColor: tokens.color.border[scheme],
82
+ borderRadius: tokens.radius.field,
83
+ };
84
+ }
85
+
86
+ /** Text on `fieldStyle`. From the same scheme as the fill, so the two cannot disagree. */
87
+ export function fieldTextStyle(scheme: Scheme) {
88
+ return textStyle("foreground", scheme);
89
+ }
90
+
91
+ /**
92
+ * `foreground` and `muted` are the web `text-foreground` and
93
+ * `text-muted-foreground`; `overlayMuted` is the web `DialogDescription`.
94
+ */
95
+ export type TextTone = "foreground" | "muted" | "overlayMuted";
96
+
97
+ /**
98
+ * Text colour as a style. Where an app defines the NativeWind `foreground` key
99
+ * with a nested `dark` shade, `text-foreground` keeps the light value in dark
100
+ * mode, so class-coloured text was near-black on dark surfaces.
101
+ */
102
+ export function textStyle(tone: TextTone, scheme: Scheme) {
103
+ return {
104
+ color: tone === "overlayMuted" ? palette.overlayMuted[scheme] : tokens.color[tone][scheme],
105
+ };
106
+ }
107
+
108
+ /** The web `font-medium` and `font-semibold`. */
109
+ export type TypeWeight = "medium" | "semibold";
110
+
111
+ const FONT_WEIGHT = { medium: "500", semibold: "600" } as const;
112
+
113
+ /**
114
+ * Font weight as a style, set beside the `font-instrument-sans-*` class with
115
+ * the same weight. Expo web and many apps map every one of those classes to
116
+ * the one `Instrument Sans` family with no weight, so the class alone draws
117
+ * Regular. There is no `bold`: the web titles are semibold, and on Android 700
118
+ * can add a synthetic bold to a face that is already bold.
119
+ */
120
+ export function typeStyle(weight: TypeWeight) {
121
+ return { fontWeight: FONT_WEIGHT[weight] };
122
+ }
123
+
124
+ /** Tailwind `text-*` sizes with their default line heights, in points. */
125
+ const TEXT_SIZE = {
126
+ "text-xs": { fontSize: 12, lineHeight: 16 },
127
+ "text-sm": { fontSize: 14, lineHeight: 20 },
128
+ "text-base": { fontSize: 16, lineHeight: 24 },
129
+ "text-lg": { fontSize: 18, lineHeight: 28 },
130
+ "text-xl": { fontSize: 20, lineHeight: 28 },
131
+ "text-2xl": { fontSize: 24, lineHeight: 32 },
132
+ } as const;
133
+
134
+ export type TextSize = keyof typeof TEXT_SIZE;
135
+
136
+ /** Tailwind `leading-tight` and `leading-snug`, as multiples of the size. */
137
+ const LEADING = { tight: 1.25, snug: 1.375 } as const;
138
+
139
+ /**
140
+ * Font size and line height of a `text-*` class as a style, set beside the
141
+ * class. On Expo web at 390 wide (0.3.6 gallery), `Button` labels with
142
+ * `text-sm` measured 16, so the class alone does not always reach the text.
143
+ * `"tight"` and `"snug"` are the Tailwind `leading-tight` (1.25 × the size)
144
+ * and `leading-snug` (1.375 ×).
145
+ */
146
+ export function sizeStyle(size: TextSize, leading?: "tight" | "snug") {
147
+ const { fontSize, lineHeight } = TEXT_SIZE[size];
148
+ return { fontSize, lineHeight: leading ? fontSize * LEADING[leading] : lineHeight };
149
+ }
150
+
151
+ /** Tailwind `tracking-tight`, in em. */
152
+ const TRACKING = { tight: -0.025 } as const;
153
+
154
+ /**
155
+ * Letter spacing of a `tracking-*` class at `fontSize`, as a style in points.
156
+ * React Native has no `em`: `tracking-tight` (-0.025em) at 20 is -0.5.
157
+ */
158
+ export function trackingStyle(fontSize: number, tracking: keyof typeof TRACKING) {
159
+ return { letterSpacing: fontSize * TRACKING[tracking] };
160
+ }
161
+
162
+ /**
163
+ * The web page title, `text-xl font-semibold leading-tight tracking-tight` on
164
+ * the `Breadcrumb` nav (`breadcrumb.tsx`), with the Instrument Sans semibold
165
+ * face for `font-semibold`. `PageTitle` sets `pageTitleStyle()` beside it.
166
+ */
167
+ export const PAGE_TITLE_CLASS = "font-instrument-sans-semibold text-xl leading-tight tracking-tight";
168
+
169
+ /**
170
+ * `PAGE_TITLE_CLASS` as a style: Instrument Sans 600 at 20 / 25 with -0.5
171
+ * letter spacing. On Android and Expo web the face class alone can draw
172
+ * Regular (see `typeStyle`).
173
+ */
174
+ export function pageTitleStyle() {
175
+ const size = sizeStyle("text-xl", "tight");
176
+ return { ...size, ...typeStyle("semibold"), ...trackingStyle(size.fontSize, "tight") };
177
+ }
178
+
179
+ /**
180
+ * Height of the `PageHeader` row below the top inset, in points. A minimum,
181
+ * so the title sits at the same height with or without an action, and still
182
+ * grows with the system font size. Native only: the web title has no row.
183
+ */
184
+ export const PAGE_HEADER_HEIGHT = 56;
185
+
186
+ /**
187
+ * The web `COMPACT_OVERLAY_QUERY`, `(max-width: 639px), (max-height: 500px)`:
188
+ * phones, and short landscape windows such as 844 × 390.
189
+ */
190
+ export const COMPACT_OVERLAY = { maxWidth: 639, maxHeight: 500 } as const;
191
+
192
+ /** Whether a window of `width` × `height` points is compact: `DialogContent` is a bottom sheet there. */
193
+ export function isCompactOverlay(width: number, height: number) {
194
+ return width <= COMPACT_OVERLAY.maxWidth || height <= COMPACT_OVERLAY.maxHeight;
195
+ }
196
+
197
+ /**
198
+ * The web `Card` root (`rounded-lg bg-surface-card`): a flat fill at radius 16,
199
+ * with no border and no shadow. Also `SettingsSection`. A flat object, like
200
+ * `fieldStyle`.
201
+ */
202
+ export function cardStyle(scheme: Scheme) {
203
+ return {
204
+ backgroundColor: tokens.color.surfaceCard[scheme],
205
+ borderRadius: tokens.radius.lg,
206
+ };
207
+ }
208
+
209
+ /** The smallest touch target on a phone, in points (Apple's 44; Material asks 48). */
210
+ export const MIN_TOUCH_TARGET = 44;
211
+
212
+ /**
213
+ * `hitSlop` that makes a control painted `width` × `height` points reach
214
+ * `MIN_TOUCH_TARGET` on each axis, so it can keep the web size on screen. An
215
+ * axis that is already large enough gets 0.
216
+ */
217
+ export function touchSlop(width: number, height: number = width) {
218
+ const pad = (size: number) => Math.max(0, Math.ceil((MIN_TOUCH_TARGET - size) / 2));
219
+ const x = pad(width);
220
+ const y = pad(height);
221
+ return { top: y, bottom: y, left: x, right: x };
222
+ }
223
+
224
+ export type SearchInputSize = "sm" | "md";
225
+
226
+ /**
227
+ * Field, icon and clear-button metrics for `SearchInput`, in points. Without
228
+ * larger text these are the web sizes (`search-input.tsx`, `clear-input.tsx`):
229
+ * `sm` is `h-8 pl-8 text-sm` with the icon at `left-2`, `md` is `h-10 pl-9`
230
+ * (the phone `text-base`) with the icon at `left-3`. Both have a 16 search
231
+ * icon, the field `px-3.5` at the end, and the `h-6 w-6` clear button at
232
+ * `right-2` with a 14 cross, for which a filled field reserves `pr-9`.
233
+ * `height` locks the field at the web height, border included (the web
234
+ * `h-8` and `h-10` are border-box), so the line height of the `text-*` class
235
+ * cannot grow it. `SearchInput` scales it with the system font size
236
+ * (`scaledFieldHeight`), so a large Dynamic Type size still grows the field.
237
+ * The padding leaves room for the line inside the lock: `sm` pads 4 above and
238
+ * below and `md` 7, not the web 8, because a native field adds its padding to
239
+ * the line height (8 drew `md` 42 high on Expo web). With larger text both
240
+ * sizes are the larger `Input` (64 high, `text-xl`), with a minimum height
241
+ * and no lock.
242
+ */
243
+ export function searchInputSize(size: SearchInputSize, largerText: boolean) {
244
+ if (largerText) {
245
+ return {
246
+ minHeight: 64,
247
+ height: undefined,
248
+ paddingY: 16,
249
+ paddingLeft: 48,
250
+ paddingRight: 16,
251
+ filledPaddingRight: 48,
252
+ text: "text-xl",
253
+ icon: 24,
254
+ iconLeft: 16,
255
+ clear: 32,
256
+ clearIcon: 20,
257
+ clearRight: 12,
258
+ } as const;
259
+ }
260
+ const shared = {
261
+ paddingRight: 14,
262
+ filledPaddingRight: 36,
263
+ icon: 16,
264
+ clear: 24,
265
+ clearIcon: 14,
266
+ clearRight: 8,
267
+ } as const;
268
+ return size === "md"
269
+ ? { ...shared, minHeight: 40, height: 40, paddingY: 7, paddingLeft: 36, text: "text-base", iconLeft: 12 } as const
270
+ : { ...shared, minHeight: 32, height: 32, paddingY: 4, paddingLeft: 32, text: "text-sm", iconLeft: 8 } as const;
271
+ }
272
+
273
+ /**
274
+ * A locked field height at the system font scale (`useWindowDimensions().fontScale`):
275
+ * `height` up to a scale of 1, and `height` × the scale above it, as a web
276
+ * `rem` height grows with the browser text size. `undefined` (no lock) stays
277
+ * `undefined`.
278
+ */
279
+ export function scaledFieldHeight(height: number | undefined, fontScale: number) {
280
+ return height === undefined ? undefined : height * Math.max(1, fontScale);
281
+ }
282
+
283
+ export type TabsVariant = "pills" | "segmented" | "soft";
284
+
285
+ export type TabColors = {
286
+ backgroundColor: string;
287
+ color: string;
288
+ };
289
+
290
+ /**
291
+ * Fill and label colour of one tab, from the web tab variables
292
+ * (`tabs-pills.css`). Unselected: no fill, `--muted` label. Selected: a
293
+ * `--default` pill or a `--segment` segment with the `--segment-foreground`
294
+ * label (the neutral primary, not the accent). `soft` is the web soft trigger
295
+ * (`bg-surface-secondary text-foreground`). Fill and label come from one
296
+ * scheme, so a selected label cannot match its fill.
297
+ */
298
+ export function tabColors(variant: TabsVariant, selected: boolean, scheme: Scheme): TabColors {
299
+ if (!selected) return { backgroundColor: "transparent", color: tokens.color.muted[scheme] };
300
+ switch (variant) {
301
+ case "segmented":
302
+ return { backgroundColor: palette.segment[scheme], color: palette.segmentForeground[scheme] };
303
+ case "soft":
304
+ return {
305
+ backgroundColor: tokens.color.surfaceSecondary[scheme],
306
+ color: tokens.color.foreground[scheme],
307
+ };
308
+ default:
309
+ return { backgroundColor: palette.default[scheme], color: palette.segmentForeground[scheme] };
310
+ }
311
+ }
312
+
313
+ export type TabsSize = "default" | "sm";
314
+
315
+ /**
316
+ * Trigger box and type for `TabsTrigger` and `PillTabs`. Without larger text
317
+ * these are the web sizes: `pills` and `segmented` are the web `.t-tab` (30
318
+ * high, `px-3 py-1`, `text-sm`), `soft` the web soft trigger (`px-4 py-2
319
+ * text-sm`) and `sm` the web `sm` list (`px-2.5 py-1 text-xs`). With larger
320
+ * text they are the 0.3.4 sizes. `minHeight` is a minimum, so a large Dynamic
321
+ * Type size grows the trigger instead of clipping its label.
322
+ */
323
+ export function tabTriggerSize(variant: TabsVariant, size: TabsSize, largerText: boolean) {
324
+ if (largerText) {
325
+ return size === "sm"
326
+ ? { minHeight: 48, className: "px-4 py-2", textClassName: "text-lg" }
327
+ : { minHeight: 56, className: "px-4 py-3", textClassName: "text-xl" };
328
+ }
329
+ if (size === "sm") return { minHeight: 30, className: "px-2.5 py-1", textClassName: "text-xs" };
330
+ return variant === "soft"
331
+ ? { minHeight: 36, className: "px-4 py-2", textClassName: "text-sm" }
332
+ : { minHeight: 30, className: "px-3 py-1", textClassName: "text-sm" };
333
+ }
334
+
335
+ /** The segmented track, web `--tabs-bar-bg` (`--default`). */
336
+ export function tabTrackStyle(scheme: Scheme) {
337
+ return { backgroundColor: palette.default[scheme] };
338
+ }
339
+
340
+ /**
341
+ * Box and check colours, from the web `Checkbox` classes: `bg-field-background
342
+ * border-border`, and `bg-primary border-primary text-primary-foreground` when
343
+ * checked (also when indeterminate).
344
+ */
345
+ export function checkboxColors(checked: boolean, scheme: Scheme) {
346
+ return checked
347
+ ? {
348
+ backgroundColor: palette.primary[scheme],
349
+ borderColor: palette.primary[scheme],
350
+ color: palette.primaryForeground[scheme],
351
+ }
352
+ : {
353
+ backgroundColor: tokens.color.surface[scheme],
354
+ borderColor: tokens.color.border[scheme],
355
+ color: palette.primaryForeground[scheme],
356
+ };
357
+ }
358
+
359
+ /**
360
+ * Track and thumb size classes, and the track size in points for the hit slop.
361
+ * Without larger text it is the web `Switch`: a `h-5 w-9` track (20 × 36,
362
+ * with a 1 border) and a `size-4` thumb that moves `translate-x-4`. With larger
363
+ * text it is the 0.3.4 size. `justify-center` on the track centres the thumb.
364
+ */
365
+ export function switchSize(largerText: boolean) {
366
+ return largerText
367
+ ? { track: "h-8 w-14", thumb: "h-7 w-7", on: "translate-x-6", width: 56, height: 32 }
368
+ : { track: "h-5 w-9", thumb: "h-4 w-4", on: "translate-x-4", width: 36, height: 20 };
369
+ }
370
+
371
+ /**
372
+ * Track and thumb colours, from the web `Switch` classes. The light off thumb
373
+ * is white (web `bg-background`) so it shows on the grey track. Flat objects,
374
+ * for the same reason as `fieldStyle`.
375
+ */
376
+ export function switchColors(checked: boolean, scheme: Scheme) {
377
+ if (checked) {
378
+ return {
379
+ track: { backgroundColor: palette.primary[scheme], borderColor: palette.primary[scheme] },
380
+ thumb: { backgroundColor: palette.primaryForeground[scheme] },
381
+ };
382
+ }
383
+ return scheme === "dark"
384
+ ? {
385
+ track: {
386
+ backgroundColor: withAlpha(tokens.color.muted.dark, 0.4),
387
+ borderColor: withAlpha(tokens.color.muted.dark, 0.6),
388
+ },
389
+ thumb: { backgroundColor: tokens.color.foreground.dark },
390
+ }
391
+ : {
392
+ track: { backgroundColor: palette.default.light, borderColor: tokens.color.border.light },
393
+ thumb: { backgroundColor: tokens.color.surface.light },
394
+ };
395
+ }
@@ -0,0 +1,20 @@
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
+ export type DialogLayout = { compact: boolean; stacked: boolean };
12
+
13
+ export const DialogLayoutContext = createContext<DialogLayout>({
14
+ compact: false,
15
+ stacked: false,
16
+ });
17
+
18
+ export function useDialogLayout(): DialogLayout {
19
+ return useContext(DialogLayoutContext);
20
+ }
@@ -0,0 +1,3 @@
1
+ // `@vvedantb/native-ui/ambient`. Loads `expo-linear-gradient` only through its
2
+ // optional-peer loader; without it the background is solid.
3
+ export { AmbientBackground } from "../AmbientBackground";
@@ -0,0 +1,2 @@
1
+ // `@vvedantb/native-ui/bottom-sheet`. Imports `@gorhom/bottom-sheet`.
2
+ export { BottomSheetContainer } from "../BottomSheetContainer";
@@ -0,0 +1,2 @@
1
+ // `@vvedantb/native-ui/brand`: the family app marks and their pure data.
2
+ export * from "../brand";
@@ -0,0 +1,3 @@
1
+ // `@vvedantb/native-ui/button`.
2
+ export { Button } from "../Button";
3
+ export type { ButtonProps, ButtonSize, ButtonVariant } from "../Button";
@@ -0,0 +1,3 @@
1
+ // `@vvedantb/native-ui/date-time`. Imports
2
+ // `@react-native-community/datetimepicker` and `dayjs`.
3
+ export { DateTimeField } from "../DateTimeField";
@@ -0,0 +1,24 @@
1
+ // `@vvedantb/native-ui/floating`: floating surfaces, glass blur targets and
2
+ // metrics. Reaches `expo-blur` only through its optional-peer loader.
3
+ export {
4
+ FLOATING_INSET,
5
+ FLOATING_TAB_BAR_HEIGHT,
6
+ FLOATING_TAB_BAR_HEIGHT_LARGE,
7
+ FLOATING_RADIUS,
8
+ FLOATING_ROW_RADIUS,
9
+ FLOATING_PILL_RADIUS,
10
+ floatingShadow,
11
+ floatingHairline,
12
+ floatingFill,
13
+ floatingSolidFill,
14
+ tabBarReserve,
15
+ } from "../floating";
16
+ export { useReduceTransparency } from "../useReduceTransparency";
17
+ export { FloatingSurface, GlassSheetBackground } from "../FloatingSurface";
18
+ export {
19
+ LiquidGlass,
20
+ GlassBlurProvider,
21
+ GlassBlurTarget,
22
+ } from "../LiquidGlass";
23
+ export type { GlassBlurTargetProps } from "../LiquidGlass";
24
+ export { FloatingFAB } from "../FloatingFAB";
@@ -0,0 +1,10 @@
1
+ // `@vvedantb/native-ui/layout`: screen containers, page headers and settings
2
+ // chrome.
3
+ export { SafeAreaContainer } from "../SafeAreaContainer";
4
+ export { ScreenContainer } from "../ScreenContainer";
5
+ export { PageTitle, PageHeader } from "../PageTitle";
6
+ export type { PageTitleProps, PageHeaderProps } from "../PageTitle";
7
+ export { SettingsSection } from "../settings/SettingsSection";
8
+ export { SettingsField } from "../settings/SettingsField";
9
+ export { SettingsToggleRow } from "../settings/SettingsToggleRow";
10
+ export { SettingsStack } from "../settings/SettingsStack";
@@ -0,0 +1,10 @@
1
+ // `@vvedantb/native-ui/marketing`: the upgrade card and pricing modal.
2
+ export { UpgradeToProCard } from "../marketing/UpgradeToProCard";
3
+ export type { UpgradeToProCardProps } from "../marketing/UpgradeToProCard";
4
+ export { PricingModal } from "../marketing/PricingModal";
5
+ export type { PricingModalProps } from "../marketing/PricingModal";
6
+ export type {
7
+ PricingModalPeriod,
8
+ PricingPlan,
9
+ PricingPlanBenefit,
10
+ } from "../marketing/pricing";
@@ -0,0 +1,4 @@
1
+ // `@vvedantb/native-ui/page-title`: `PageTitle` and `PageHeader` on their own.
2
+ // Reaches no optional peer, not even a loader.
3
+ export { PageTitle, PageHeader } from "../PageTitle";
4
+ export type { PageTitleProps, PageHeaderProps } from "../PageTitle";
@@ -0,0 +1,99 @@
1
+ // `@vvedantb/native-ui/primitives`: fields, display, tabs, overlays and
2
+ // toasts. `BottomSheetContainer`, `DateTimeField` and `AmbientBackground` are
3
+ // not here; they have their own entries.
4
+ export { Button } from "../Button";
5
+ export type { ButtonProps, ButtonSize, ButtonVariant } from "../Button";
6
+ export { Input } from "../Input";
7
+ export type { InputProps } from "../Input";
8
+ export { SearchInput } from "../SearchInput";
9
+ export type { SearchInputProps } from "../SearchInput";
10
+ export { Textarea } from "../Textarea";
11
+ export type { TextareaProps } from "../Textarea";
12
+ export { Label } from "../Label";
13
+ export type { LabelProps } from "../Label";
14
+ export { Select } from "../Select";
15
+ export type { SelectOption, SelectProps } from "../Select";
16
+ export { Switch } from "../Switch";
17
+ export type { SwitchProps } from "../Switch";
18
+ export { LabeledSwitchRow } from "../LabeledSwitchRow";
19
+ export type { LabeledSwitchRowProps } from "../LabeledSwitchRow";
20
+ export { Checkbox } from "../Checkbox";
21
+ export type { CheckboxProps } from "../Checkbox";
22
+ export { Badge } from "../Badge";
23
+ export type { BadgeProps, BadgeVariant } from "../Badge";
24
+ export { StatusDot } from "../StatusDot";
25
+ export type { StatusDotProps, StatusTone } from "../StatusDot";
26
+ export { Separator } from "../Separator";
27
+ export type { SeparatorProps } from "../Separator";
28
+ export { Progress } from "../Progress";
29
+ export type { ProgressProps } from "../Progress";
30
+ export { Spinner, CenteredSpinner } from "../Spinner";
31
+ export type { SpinnerProps } from "../Spinner";
32
+ export { Avatar, AvatarImage, AvatarFallback } from "../Avatar";
33
+ export type { AvatarProps, AvatarImageProps, AvatarFallbackProps } from "../Avatar";
34
+ export { initials } from "../initials";
35
+ export { Chip } from "../Chip";
36
+ export { PillTabs } from "../PillTabs";
37
+ export { Tabs, TabsList, TabsTrigger, TabsContent } from "../Tabs";
38
+ export type {
39
+ TabsProps,
40
+ TabsListProps,
41
+ TabsListVariant,
42
+ TabsTriggerProps,
43
+ TabsContentProps,
44
+ } from "../Tabs";
45
+ export { ListRow } from "../ListRow";
46
+ export { HeaderIconButton } from "../HeaderIconButton";
47
+ export {
48
+ Card,
49
+ CardHeader,
50
+ CardTitle,
51
+ CardDescription,
52
+ CardContent,
53
+ CardFooter,
54
+ } from "../Card";
55
+ export { Skeleton, CardSkeleton } from "../Skeleton";
56
+ export { EmptyState } from "../EmptyState";
57
+ export type { EmptyStateVariant } from "../EmptyState";
58
+ export {
59
+ Dialog,
60
+ DialogTrigger,
61
+ DialogClose,
62
+ DialogContent,
63
+ DialogHeader,
64
+ DialogTitle,
65
+ DialogDescription,
66
+ DialogBody,
67
+ DialogFooter,
68
+ } from "../Dialog";
69
+ export type { DialogProps, DialogContentProps } from "../Dialog";
70
+ export {
71
+ AlertDialog,
72
+ AlertDialogTrigger,
73
+ AlertDialogContent,
74
+ AlertDialogHeader,
75
+ AlertDialogTitle,
76
+ AlertDialogDescription,
77
+ AlertDialogFooter,
78
+ AlertDialogAction,
79
+ AlertDialogCancel,
80
+ } from "../AlertDialog";
81
+ export type { AlertDialogProps, AlertDialogContentProps } from "../AlertDialog";
82
+ export { ConfirmDialog } from "../ConfirmDialog";
83
+ export type { ConfirmDialogProps } from "../ConfirmDialog";
84
+ export {
85
+ Sheet,
86
+ SheetTrigger,
87
+ SheetClose,
88
+ SheetContent,
89
+ SheetHandle,
90
+ SheetHeader,
91
+ SheetTitle,
92
+ SheetDescription,
93
+ SheetBody,
94
+ SheetFooter,
95
+ } from "../Sheet";
96
+ export type { SheetProps, SheetContentProps } from "../Sheet";
97
+ export { Toaster } from "../Toast";
98
+ export { toast } from "../toastStore";
99
+ export type { ToastOptions, ToastVariant } from "../toastStore";
@@ -0,0 +1,4 @@
1
+ // `@vvedantb/native-ui/search-input`: `SearchInput` on its own. Reaches no
2
+ // optional peer, not even a loader.
3
+ export { SearchInput } from "../SearchInput";
4
+ export type { SearchInputProps } from "../SearchInput";
@@ -0,0 +1,10 @@
1
+ // `@vvedantb/native-ui/tab-bar`. Does not import `expo-router`; reaches
2
+ // `expo-blur` and `expo-haptics` only through their optional-peer loaders.
3
+ export { FloatingTabBar } from "../FloatingTabBar";
4
+ export type { FloatingTabBarProps } from "../FloatingTabBar";
5
+ export {
6
+ FLOATING_INSET,
7
+ FLOATING_TAB_BAR_HEIGHT,
8
+ FLOATING_TAB_BAR_HEIGHT_LARGE,
9
+ tabBarReserve,
10
+ } from "../floating";
@@ -0,0 +1,15 @@
1
+ // `@vvedantb/native-ui/theme`: the provider, tokens and colour helpers.
2
+ export { cn } from "../cn";
3
+ export {
4
+ tokens,
5
+ NativeUiProvider,
6
+ useNativeUi,
7
+ useLargerText,
8
+ useAccentColor,
9
+ DEFAULT_ACCENT,
10
+ } from "../theme";
11
+ export type { NativeUiTheme } from "../theme";
12
+ export { palette, withAlpha } from "../palette";
13
+ export type { Palette } from "../palette";
14
+ export { fontClass } from "../typography";
15
+ export { PINK_50, PINK_100, PINK_600, PINK_700, NEUTRAL_100, NEUTRAL_500, NEUTRAL_900, Colors } from "../colors";
@@ -0,0 +1,6 @@
1
+ // `@vvedantb/native-ui/toast`: `toast()` and `<Toaster />`. Reaches no heavy
2
+ // optional peer, not even a loader (the toast surface is flat from 0.3.12): no
3
+ // `expo-blur`, bottom sheet, date picker, `dayjs` or `expo-router`.
4
+ export { Toaster } from "../Toast";
5
+ export { toast } from "../toastStore";
6
+ export type { ToastOptions, ToastVariant } from "../toastStore";