@vvedantb/native-ui 0.2.1 → 0.3.12
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/README.md +160 -9
- package/package.json +80 -2
- package/src/AlertDialog.tsx +73 -0
- package/src/AmbientBackground.tsx +26 -11
- package/src/Avatar.tsx +141 -0
- package/src/Badge.tsx +108 -0
- package/src/BottomSheetContainer.tsx +7 -1
- package/src/Button.tsx +153 -52
- package/src/Card.tsx +111 -36
- package/src/Checkbox.tsx +78 -0
- package/src/Chip.tsx +5 -3
- package/src/ConfirmDialog.tsx +105 -0
- package/src/DateTimeField.tsx +6 -2
- package/src/Dialog.tsx +253 -0
- package/src/EmptyState.tsx +122 -7
- package/src/FloatingFAB.tsx +2 -4
- package/src/FloatingSurface.tsx +31 -9
- package/src/FloatingTabBar.tsx +39 -11
- package/src/Input.tsx +15 -4
- package/src/Label.tsx +31 -0
- package/src/LabeledSwitchRow.tsx +96 -0
- package/src/LiquidGlass.tsx +2 -10
- package/src/ListRow.tsx +7 -2
- package/src/PillTabs.tsx +28 -14
- package/src/Progress.tsx +67 -0
- package/src/SearchInput.tsx +163 -0
- package/src/Select.tsx +32 -6
- package/src/Separator.tsx +38 -0
- package/src/Sheet.tsx +107 -0
- package/src/Skeleton.tsx +25 -5
- package/src/Spinner.tsx +92 -0
- package/src/StatusDot.tsx +66 -0
- package/src/Switch.tsx +24 -7
- package/src/Tabs.tsx +190 -0
- package/src/Textarea.tsx +47 -0
- package/src/Toast.tsx +170 -0
- package/src/blur.ts +48 -0
- package/src/brand/AppIcons.tsx +106 -0
- package/src/brand/AppMark.tsx +83 -0
- package/src/brand/AppWordmark.tsx +97 -0
- package/src/brand/familyApps.ts +24 -0
- package/src/brand/index.ts +34 -0
- package/src/brand/svg.ts +263 -0
- package/src/brand/vProAppBenefits.ts +22 -0
- package/src/controlStyles.ts +360 -0
- package/src/dialogLayout.ts +20 -0
- package/src/entries/ambient.ts +3 -0
- package/src/entries/bottom-sheet.ts +2 -0
- package/src/entries/brand.ts +2 -0
- package/src/entries/button.ts +3 -0
- package/src/entries/date-time.ts +3 -0
- package/src/entries/floating.ts +24 -0
- package/src/entries/layout.ts +7 -0
- package/src/entries/marketing.ts +10 -0
- package/src/entries/primitives.ts +99 -0
- package/src/entries/search-input.ts +4 -0
- package/src/entries/tab-bar.ts +10 -0
- package/src/entries/theme.ts +15 -0
- package/src/entries/toast.ts +6 -0
- package/src/examples/ParityGallery.tsx +496 -0
- package/src/floating.ts +5 -1
- package/src/glassBlur.tsx +95 -0
- package/src/gradient.ts +21 -0
- package/src/haptics.ts +22 -2
- package/src/index.ts +99 -2
- package/src/initials.ts +10 -0
- package/src/marketing/PricingModal.tsx +410 -0
- package/src/marketing/UpgradeToProCard.tsx +103 -0
- package/src/marketing/pricing.ts +76 -0
- package/src/palette.ts +43 -0
- package/src/primitives/input.tsx +9 -2
- package/src/primitives/native-only-animated-view.tsx +10 -3
- package/src/primitives/overlay.tsx +179 -0
- package/src/primitives/select.tsx +25 -12
- package/src/primitives/switch.tsx +12 -13
- package/src/primitives/use-controllable-state.ts +24 -0
- package/src/settings/SettingsField.tsx +11 -2
- package/src/settings/SettingsSection.tsx +20 -6
- package/src/settings/SettingsToggleRow.tsx +7 -2
- package/src/tabBarRoutes.ts +52 -0
- package/src/toastChrome.ts +103 -0
- package/src/toastStore.ts +86 -0
- package/src/tokens.ts +19 -14
- package/src/typography.ts +24 -0
- package/src/useReduceMotion.ts +24 -0
- package/src/useScheme.ts +6 -0
|
@@ -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,7 @@
|
|
|
1
|
+
// `@vvedantb/native-ui/layout`: screen containers and settings chrome.
|
|
2
|
+
export { SafeAreaContainer } from "../SafeAreaContainer";
|
|
3
|
+
export { ScreenContainer } from "../ScreenContainer";
|
|
4
|
+
export { SettingsSection } from "../settings/SettingsSection";
|
|
5
|
+
export { SettingsField } from "../settings/SettingsField";
|
|
6
|
+
export { SettingsToggleRow } from "../settings/SettingsToggleRow";
|
|
7
|
+
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,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,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";
|
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
// Reference screen for the components that mirror `@vvedantb/ui`. Render it in
|
|
2
|
+
// an Expo route to compare against the web: it needs the same NativeWind setup
|
|
3
|
+
// as the rest of the package and a `SafeAreaProvider` above it. It always
|
|
4
|
+
// renders the default sizes (larger text off), as the web does. Prices below
|
|
5
|
+
// are placeholders, not real plan prices.
|
|
6
|
+
import { useState, type ReactNode } from "react";
|
|
7
|
+
import { ScrollView, Text, View, type TextProps } from "react-native";
|
|
8
|
+
import { IconInbox } from "@tabler/icons-react-native";
|
|
9
|
+
import {
|
|
10
|
+
APP_ICONS,
|
|
11
|
+
APP_NAMES,
|
|
12
|
+
AlertDialog,
|
|
13
|
+
AlertDialogAction,
|
|
14
|
+
AlertDialogCancel,
|
|
15
|
+
AlertDialogContent,
|
|
16
|
+
AlertDialogDescription,
|
|
17
|
+
AlertDialogFooter,
|
|
18
|
+
AlertDialogHeader,
|
|
19
|
+
AlertDialogTitle,
|
|
20
|
+
AlertDialogTrigger,
|
|
21
|
+
AppMark,
|
|
22
|
+
AppWordmark,
|
|
23
|
+
Avatar,
|
|
24
|
+
AvatarFallback,
|
|
25
|
+
Badge,
|
|
26
|
+
Button,
|
|
27
|
+
Card,
|
|
28
|
+
CardContent,
|
|
29
|
+
CardDescription,
|
|
30
|
+
CardFooter,
|
|
31
|
+
CardHeader,
|
|
32
|
+
CardTitle,
|
|
33
|
+
CenteredSpinner,
|
|
34
|
+
Checkbox,
|
|
35
|
+
ConfirmDialog,
|
|
36
|
+
Dialog,
|
|
37
|
+
DialogBody,
|
|
38
|
+
DialogClose,
|
|
39
|
+
DialogContent,
|
|
40
|
+
DialogDescription,
|
|
41
|
+
DialogFooter,
|
|
42
|
+
DialogHeader,
|
|
43
|
+
DialogTitle,
|
|
44
|
+
DialogTrigger,
|
|
45
|
+
EmptyState,
|
|
46
|
+
Input,
|
|
47
|
+
Label,
|
|
48
|
+
LabeledSwitchRow,
|
|
49
|
+
NativeUiProvider,
|
|
50
|
+
PricingModal,
|
|
51
|
+
Progress,
|
|
52
|
+
SearchInput,
|
|
53
|
+
Select,
|
|
54
|
+
Separator,
|
|
55
|
+
Sheet,
|
|
56
|
+
SheetContent,
|
|
57
|
+
SheetDescription,
|
|
58
|
+
SheetFooter,
|
|
59
|
+
SheetHeader,
|
|
60
|
+
SheetTitle,
|
|
61
|
+
SheetTrigger,
|
|
62
|
+
Skeleton,
|
|
63
|
+
Spinner,
|
|
64
|
+
StatusDot,
|
|
65
|
+
Switch,
|
|
66
|
+
Tabs,
|
|
67
|
+
TabsContent,
|
|
68
|
+
TabsList,
|
|
69
|
+
TabsTrigger,
|
|
70
|
+
Textarea,
|
|
71
|
+
Toaster,
|
|
72
|
+
UpgradeToProCard,
|
|
73
|
+
initials,
|
|
74
|
+
toast,
|
|
75
|
+
tokens,
|
|
76
|
+
useNativeUi,
|
|
77
|
+
type BadgeVariant,
|
|
78
|
+
type ButtonVariant,
|
|
79
|
+
type PricingPlan,
|
|
80
|
+
type StatusTone,
|
|
81
|
+
} from "../index";
|
|
82
|
+
import { typeStyle } from "../controlStyles";
|
|
83
|
+
import { useScheme } from "../useScheme";
|
|
84
|
+
|
|
85
|
+
const BUTTON_VARIANTS: ButtonVariant[] = [
|
|
86
|
+
"default",
|
|
87
|
+
"secondary",
|
|
88
|
+
"outline",
|
|
89
|
+
"destructive",
|
|
90
|
+
"ghost",
|
|
91
|
+
"link",
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
const BADGE_VARIANTS: BadgeVariant[] = [
|
|
95
|
+
"default",
|
|
96
|
+
"secondary",
|
|
97
|
+
"destructive",
|
|
98
|
+
"destructive-soft",
|
|
99
|
+
"outline",
|
|
100
|
+
"success",
|
|
101
|
+
"warning",
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
const STATUS_TONES: StatusTone[] = [
|
|
105
|
+
"progress",
|
|
106
|
+
"business-review",
|
|
107
|
+
"code-review",
|
|
108
|
+
"done",
|
|
109
|
+
"cancelled",
|
|
110
|
+
"active",
|
|
111
|
+
"critical",
|
|
112
|
+
"neutral",
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
// Space after the last section, so Card, Upgrade and Overlays can scroll to the
|
|
116
|
+
// top of an 844-tall phone for screenshot crops.
|
|
117
|
+
const GALLERY_END_SPACE = 720;
|
|
118
|
+
|
|
119
|
+
const PLANS: PricingPlan[] = [
|
|
120
|
+
{
|
|
121
|
+
id: "free",
|
|
122
|
+
name: "Free",
|
|
123
|
+
price: { month: "£0", year: "£0" },
|
|
124
|
+
current: true,
|
|
125
|
+
benefits: [{ label: "Core features" }],
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
id: "pro",
|
|
129
|
+
name: "V Pro",
|
|
130
|
+
price: { month: "£X", year: "£Y" },
|
|
131
|
+
featured: true,
|
|
132
|
+
intro: "Everything in Free, and:",
|
|
133
|
+
},
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
/** Body text in the scheme's foreground (a style, so it reads in dark mode too). */
|
|
137
|
+
function GalleryText({ className, style, ...props }: TextProps & { className?: string }) {
|
|
138
|
+
const scheme = useScheme();
|
|
139
|
+
return (
|
|
140
|
+
<Text
|
|
141
|
+
className={className ?? "font-instrument-sans text-base"}
|
|
142
|
+
style={[{ color: tokens.color.foreground[scheme] }, style]}
|
|
143
|
+
{...props}
|
|
144
|
+
/>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function Section({ title, children }: { title: string; children: ReactNode }) {
|
|
149
|
+
return (
|
|
150
|
+
<View className="gap-3">
|
|
151
|
+
<GalleryText
|
|
152
|
+
accessibilityRole="header"
|
|
153
|
+
className="font-instrument-sans-semibold text-lg"
|
|
154
|
+
style={typeStyle("semibold")}
|
|
155
|
+
>
|
|
156
|
+
{title}
|
|
157
|
+
</GalleryText>
|
|
158
|
+
{children}
|
|
159
|
+
</View>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function ParityGallery() {
|
|
164
|
+
const { accentColor } = useNativeUi();
|
|
165
|
+
return (
|
|
166
|
+
<NativeUiProvider largerText={false} accentColor={accentColor}>
|
|
167
|
+
<GalleryScreen />
|
|
168
|
+
</NativeUiProvider>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function GalleryScreen() {
|
|
173
|
+
const [checked, setChecked] = useState<boolean | "indeterminate">("indeterminate");
|
|
174
|
+
const [switchOn, setSwitchOn] = useState(true);
|
|
175
|
+
const [colour, setColour] = useState<string | null | undefined>(undefined);
|
|
176
|
+
const [emptySearch, setEmptySearch] = useState("");
|
|
177
|
+
const [search, setSearch] = useState("Pink");
|
|
178
|
+
const [progress, setProgress] = useState(40);
|
|
179
|
+
const [pricingOpen, setPricingOpen] = useState(false);
|
|
180
|
+
const [confirmOpen, setConfirmOpen] = useState(false);
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<View className="flex-1">
|
|
184
|
+
<ScrollView
|
|
185
|
+
// So the SearchInput clear button takes the first tap while the keyboard is up.
|
|
186
|
+
keyboardShouldPersistTaps="handled"
|
|
187
|
+
contentContainerClassName="gap-8 px-4 pt-4"
|
|
188
|
+
contentContainerStyle={{ paddingBottom: GALLERY_END_SPACE }}
|
|
189
|
+
>
|
|
190
|
+
<Section title="Brand">
|
|
191
|
+
{(["vibot", "verve", "velth", "vmem", "vhub"] as const).map((app) => (
|
|
192
|
+
<AppWordmark key={app} app={app} />
|
|
193
|
+
))}
|
|
194
|
+
<View className="flex-row flex-wrap items-center gap-3">
|
|
195
|
+
{APP_NAMES.map((app) => {
|
|
196
|
+
const Icon = APP_ICONS[app];
|
|
197
|
+
return <Icon key={app} size={28} />;
|
|
198
|
+
})}
|
|
199
|
+
<AppMark app="vibot" variant="tile" size={40} />
|
|
200
|
+
<AppMark app="vibot" variant="tile-light" size={40} />
|
|
201
|
+
</View>
|
|
202
|
+
</Section>
|
|
203
|
+
|
|
204
|
+
<Section title="Button">
|
|
205
|
+
{/* The web gallery: a full-width column of variants, then the sm row. */}
|
|
206
|
+
<View className="gap-2">
|
|
207
|
+
{BUTTON_VARIANTS.map((variant) => (
|
|
208
|
+
<Button key={variant} variant={variant} onPress={() => toast(variant)}>
|
|
209
|
+
{variant}
|
|
210
|
+
</Button>
|
|
211
|
+
))}
|
|
212
|
+
<View className="flex-row gap-2">
|
|
213
|
+
<Button size="sm" fullWidth={false} loading>
|
|
214
|
+
Saving
|
|
215
|
+
</Button>
|
|
216
|
+
<Button size="sm" fullWidth={false} disabled>
|
|
217
|
+
Disabled
|
|
218
|
+
</Button>
|
|
219
|
+
</View>
|
|
220
|
+
</View>
|
|
221
|
+
<View className="flex-row flex-wrap items-center gap-2">
|
|
222
|
+
<Button size="sm" fullWidth={false}>
|
|
223
|
+
Small
|
|
224
|
+
</Button>
|
|
225
|
+
<Button fullWidth={false}>Default</Button>
|
|
226
|
+
<Button size="lg" fullWidth={false}>
|
|
227
|
+
Large
|
|
228
|
+
</Button>
|
|
229
|
+
</View>
|
|
230
|
+
</Section>
|
|
231
|
+
|
|
232
|
+
<Section title="Badge">
|
|
233
|
+
<View className="flex-row flex-wrap gap-2">
|
|
234
|
+
{BADGE_VARIANTS.map((variant) => (
|
|
235
|
+
<Badge key={variant} variant={variant}>
|
|
236
|
+
{variant}
|
|
237
|
+
</Badge>
|
|
238
|
+
))}
|
|
239
|
+
</View>
|
|
240
|
+
</Section>
|
|
241
|
+
|
|
242
|
+
<Section title="StatusDot">
|
|
243
|
+
<View className="flex-row flex-wrap items-center gap-3">
|
|
244
|
+
{STATUS_TONES.map((tone) => (
|
|
245
|
+
<StatusDot key={tone} tone={tone} size="md" />
|
|
246
|
+
))}
|
|
247
|
+
</View>
|
|
248
|
+
</Section>
|
|
249
|
+
|
|
250
|
+
<Section title="Fields">
|
|
251
|
+
<View className="gap-2">
|
|
252
|
+
<Label nativeID="gallery-name">Name</Label>
|
|
253
|
+
<Input placeholder="Your name" accessibilityLabel="Name" />
|
|
254
|
+
</View>
|
|
255
|
+
{/* Empty (`sm`: icon and placeholder) and filled (`md`: the clear button). */}
|
|
256
|
+
<SearchInput value={emptySearch} onChange={setEmptySearch} />
|
|
257
|
+
<SearchInput value={search} onChange={setSearch} size="md" label="Search colours" />
|
|
258
|
+
<Textarea placeholder="Notes" accessibilityLabel="Notes" />
|
|
259
|
+
<Select
|
|
260
|
+
value={colour}
|
|
261
|
+
onValueChange={setColour}
|
|
262
|
+
placeholder="Pick a colour"
|
|
263
|
+
options={[
|
|
264
|
+
{ label: "Pink", value: "pink" },
|
|
265
|
+
{ label: "None", value: null },
|
|
266
|
+
]}
|
|
267
|
+
/>
|
|
268
|
+
<View className="flex-row items-center gap-3">
|
|
269
|
+
<Checkbox
|
|
270
|
+
checked={checked}
|
|
271
|
+
onCheckedChange={setChecked}
|
|
272
|
+
accessibilityLabel="Remember me"
|
|
273
|
+
/>
|
|
274
|
+
<Label nativeID="gallery-remember">Remember me</Label>
|
|
275
|
+
<Switch checked={switchOn} onCheckedChange={setSwitchOn} />
|
|
276
|
+
</View>
|
|
277
|
+
<LabeledSwitchRow
|
|
278
|
+
label="Reminders"
|
|
279
|
+
description="A daily nudge at 9:00."
|
|
280
|
+
checked={switchOn}
|
|
281
|
+
onCheckedChange={setSwitchOn}
|
|
282
|
+
/>
|
|
283
|
+
</Section>
|
|
284
|
+
|
|
285
|
+
<Section title="Tabs">
|
|
286
|
+
<Tabs defaultValue="one">
|
|
287
|
+
<TabsList variant="segmented">
|
|
288
|
+
<TabsTrigger value="one">One</TabsTrigger>
|
|
289
|
+
<TabsTrigger value="two">Two</TabsTrigger>
|
|
290
|
+
<TabsTrigger value="three" disabled>
|
|
291
|
+
Three
|
|
292
|
+
</TabsTrigger>
|
|
293
|
+
</TabsList>
|
|
294
|
+
<TabsContent value="one">
|
|
295
|
+
<GalleryText>First panel</GalleryText>
|
|
296
|
+
</TabsContent>
|
|
297
|
+
<TabsContent value="two">
|
|
298
|
+
<GalleryText>Second panel</GalleryText>
|
|
299
|
+
</TabsContent>
|
|
300
|
+
</Tabs>
|
|
301
|
+
</Section>
|
|
302
|
+
|
|
303
|
+
<Section title="Avatar, Progress, Spinner, Skeleton">
|
|
304
|
+
<View className="flex-row items-center gap-3">
|
|
305
|
+
<Avatar>
|
|
306
|
+
<AvatarFallback>{initials("Ada Lovelace")}</AvatarFallback>
|
|
307
|
+
</Avatar>
|
|
308
|
+
<Avatar size={56}>
|
|
309
|
+
<AvatarFallback>{initials("vibot")}</AvatarFallback>
|
|
310
|
+
</Avatar>
|
|
311
|
+
<Spinner />
|
|
312
|
+
<Spinner size="lg" />
|
|
313
|
+
</View>
|
|
314
|
+
<Progress value={progress} />
|
|
315
|
+
<Button
|
|
316
|
+
variant="secondary"
|
|
317
|
+
size="sm"
|
|
318
|
+
onPress={() => setProgress((value) => (value + 20) % 120)}
|
|
319
|
+
>
|
|
320
|
+
Step progress
|
|
321
|
+
</Button>
|
|
322
|
+
<Skeleton className="h-4 w-32" />
|
|
323
|
+
<Separator />
|
|
324
|
+
<View className="h-24">
|
|
325
|
+
<CenteredSpinner label="Loading list" />
|
|
326
|
+
</View>
|
|
327
|
+
</Section>
|
|
328
|
+
|
|
329
|
+
<Section title="Card">
|
|
330
|
+
<Card title="Today" description="Card props, as before.">
|
|
331
|
+
<GalleryText>Body</GalleryText>
|
|
332
|
+
</Card>
|
|
333
|
+
<Card>
|
|
334
|
+
<CardHeader>
|
|
335
|
+
<CardTitle>Composed</CardTitle>
|
|
336
|
+
<CardDescription>Web part names inside the native card.</CardDescription>
|
|
337
|
+
</CardHeader>
|
|
338
|
+
<CardContent>
|
|
339
|
+
<GalleryText>Content</GalleryText>
|
|
340
|
+
</CardContent>
|
|
341
|
+
<CardFooter>
|
|
342
|
+
<Button size="sm" fullWidth={false} variant="secondary">
|
|
343
|
+
Later
|
|
344
|
+
</Button>
|
|
345
|
+
<Button size="sm" fullWidth={false}>
|
|
346
|
+
Save
|
|
347
|
+
</Button>
|
|
348
|
+
</CardFooter>
|
|
349
|
+
</Card>
|
|
350
|
+
</Section>
|
|
351
|
+
|
|
352
|
+
<Section title="Upgrade">
|
|
353
|
+
<UpgradeToProCard onUpgrade={() => setPricingOpen(true)} />
|
|
354
|
+
<UpgradeToProCard collapsed onUpgrade={() => setPricingOpen(true)} />
|
|
355
|
+
</Section>
|
|
356
|
+
|
|
357
|
+
<Section title="Overlays and toast">
|
|
358
|
+
<Dialog>
|
|
359
|
+
<DialogTrigger asChild>
|
|
360
|
+
<Button variant="secondary">Open dialog</Button>
|
|
361
|
+
</DialogTrigger>
|
|
362
|
+
<DialogContent>
|
|
363
|
+
<DialogHeader>
|
|
364
|
+
<DialogTitle>Rename list</DialogTitle>
|
|
365
|
+
<DialogDescription>Pick a short name.</DialogDescription>
|
|
366
|
+
</DialogHeader>
|
|
367
|
+
<DialogBody>
|
|
368
|
+
<Input placeholder="Name" accessibilityLabel="List name" />
|
|
369
|
+
</DialogBody>
|
|
370
|
+
<DialogFooter>
|
|
371
|
+
<DialogClose asChild>
|
|
372
|
+
<Button variant="secondary" shadowType="flat" fullWidth={false}>
|
|
373
|
+
Cancel
|
|
374
|
+
</Button>
|
|
375
|
+
</DialogClose>
|
|
376
|
+
<DialogClose asChild>
|
|
377
|
+
<Button
|
|
378
|
+
shadowType="flat"
|
|
379
|
+
fullWidth={false}
|
|
380
|
+
onPress={() => toast.success("Renamed")}
|
|
381
|
+
>
|
|
382
|
+
Save
|
|
383
|
+
</Button>
|
|
384
|
+
</DialogClose>
|
|
385
|
+
</DialogFooter>
|
|
386
|
+
</DialogContent>
|
|
387
|
+
</Dialog>
|
|
388
|
+
|
|
389
|
+
<AlertDialog>
|
|
390
|
+
<AlertDialogTrigger asChild>
|
|
391
|
+
<Button variant="secondary">Open alert dialog</Button>
|
|
392
|
+
</AlertDialogTrigger>
|
|
393
|
+
<AlertDialogContent>
|
|
394
|
+
<AlertDialogHeader>
|
|
395
|
+
<AlertDialogTitle>Delete this list?</AlertDialogTitle>
|
|
396
|
+
<AlertDialogDescription>This cannot be undone.</AlertDialogDescription>
|
|
397
|
+
</AlertDialogHeader>
|
|
398
|
+
<AlertDialogFooter>
|
|
399
|
+
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
400
|
+
<AlertDialogAction variant="destructive" onPress={() => toast.error("Deleted")}>
|
|
401
|
+
Delete
|
|
402
|
+
</AlertDialogAction>
|
|
403
|
+
</AlertDialogFooter>
|
|
404
|
+
</AlertDialogContent>
|
|
405
|
+
</AlertDialog>
|
|
406
|
+
|
|
407
|
+
<Button variant="secondary" onPress={() => setConfirmOpen(true)}>
|
|
408
|
+
Open confirm dialog
|
|
409
|
+
</Button>
|
|
410
|
+
<ConfirmDialog
|
|
411
|
+
open={confirmOpen}
|
|
412
|
+
onOpenChange={setConfirmOpen}
|
|
413
|
+
title="Sign out?"
|
|
414
|
+
description="You can sign back in at any time."
|
|
415
|
+
confirmLabel="Sign out"
|
|
416
|
+
variant="destructive"
|
|
417
|
+
onConfirm={() => toast.success("Signed out")}
|
|
418
|
+
/>
|
|
419
|
+
|
|
420
|
+
<Sheet>
|
|
421
|
+
<SheetTrigger asChild>
|
|
422
|
+
<Button variant="secondary">Open sheet</Button>
|
|
423
|
+
</SheetTrigger>
|
|
424
|
+
<SheetContent>
|
|
425
|
+
<SheetHeader>
|
|
426
|
+
<SheetTitle>Filters</SheetTitle>
|
|
427
|
+
<SheetDescription>Narrow the list.</SheetDescription>
|
|
428
|
+
</SheetHeader>
|
|
429
|
+
<LabeledSwitchRow
|
|
430
|
+
label="Done items"
|
|
431
|
+
checked={switchOn}
|
|
432
|
+
onCheckedChange={setSwitchOn}
|
|
433
|
+
/>
|
|
434
|
+
<SheetFooter>
|
|
435
|
+
<Button shadowType="flat">Apply</Button>
|
|
436
|
+
</SheetFooter>
|
|
437
|
+
</SheetContent>
|
|
438
|
+
</Sheet>
|
|
439
|
+
|
|
440
|
+
<View className="flex-row flex-wrap gap-2">
|
|
441
|
+
<Button size="sm" fullWidth={false} onPress={() => toast.success("Saved")}>
|
|
442
|
+
Success
|
|
443
|
+
</Button>
|
|
444
|
+
<Button size="sm" fullWidth={false} onPress={() => toast.warning("Offline")}>
|
|
445
|
+
Warning
|
|
446
|
+
</Button>
|
|
447
|
+
<Button
|
|
448
|
+
size="sm"
|
|
449
|
+
fullWidth={false}
|
|
450
|
+
onPress={() => {
|
|
451
|
+
const id = toast.loading("Syncing");
|
|
452
|
+
setTimeout(() => toast.success("Synced", { id }), 1500);
|
|
453
|
+
}}
|
|
454
|
+
>
|
|
455
|
+
Loading
|
|
456
|
+
</Button>
|
|
457
|
+
<Button
|
|
458
|
+
size="sm"
|
|
459
|
+
fullWidth={false}
|
|
460
|
+
onPress={() =>
|
|
461
|
+
toast("Archived", {
|
|
462
|
+
description: "Moved to the archive.",
|
|
463
|
+
action: { label: "Undo", onPress: () => toast.info("Restored") },
|
|
464
|
+
})
|
|
465
|
+
}
|
|
466
|
+
>
|
|
467
|
+
Action
|
|
468
|
+
</Button>
|
|
469
|
+
</View>
|
|
470
|
+
</Section>
|
|
471
|
+
|
|
472
|
+
<Section title="EmptyState">
|
|
473
|
+
<EmptyState title="Nothing yet" description="Items you add show here." compact />
|
|
474
|
+
<EmptyState.Root variant="panel">
|
|
475
|
+
<EmptyState.Icon icon={IconInbox} />
|
|
476
|
+
<EmptyState.Title>No messages</EmptyState.Title>
|
|
477
|
+
<EmptyState.Description>New messages land here.</EmptyState.Description>
|
|
478
|
+
</EmptyState.Root>
|
|
479
|
+
</Section>
|
|
480
|
+
</ScrollView>
|
|
481
|
+
|
|
482
|
+
<PricingModal
|
|
483
|
+
open={pricingOpen}
|
|
484
|
+
onClose={() => setPricingOpen(false)}
|
|
485
|
+
title="Upgrade to V Pro"
|
|
486
|
+
apps={["vibot", "verve", "velth", "vmem", "vhub"]}
|
|
487
|
+
currentApp="vibot"
|
|
488
|
+
headline="One plan. Every V app."
|
|
489
|
+
plans={PLANS}
|
|
490
|
+
finePrint="Placeholder prices for the gallery."
|
|
491
|
+
onCheckout={(plan, period) => toast(`${plan.name} (${period})`)}
|
|
492
|
+
/>
|
|
493
|
+
<Toaster />
|
|
494
|
+
</View>
|
|
495
|
+
);
|
|
496
|
+
}
|