@farcaster/snap 1.6.0 → 1.7.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.
Files changed (60) hide show
  1. package/dist/react-native/catalog-renderer.d.ts +5 -0
  2. package/dist/react-native/catalog-renderer.js +36 -0
  3. package/dist/react-native/components/snap-action-button.d.ts +2 -0
  4. package/dist/react-native/components/snap-action-button.js +68 -0
  5. package/dist/react-native/components/snap-badge.d.ts +2 -0
  6. package/dist/react-native/components/snap-badge.js +38 -0
  7. package/dist/react-native/components/snap-icon.d.ts +5 -0
  8. package/dist/react-native/components/snap-icon.js +56 -0
  9. package/dist/react-native/components/snap-image.d.ts +2 -0
  10. package/dist/react-native/components/snap-image.js +24 -0
  11. package/dist/react-native/components/snap-input.d.ts +2 -0
  12. package/dist/react-native/components/snap-input.js +36 -0
  13. package/dist/react-native/components/snap-item-group.d.ts +5 -0
  14. package/dist/react-native/components/snap-item-group.js +23 -0
  15. package/dist/react-native/components/snap-item.d.ts +5 -0
  16. package/dist/react-native/components/snap-item.js +45 -0
  17. package/dist/react-native/components/snap-progress.d.ts +2 -0
  18. package/dist/react-native/components/snap-progress.js +26 -0
  19. package/dist/react-native/components/snap-separator.d.ts +2 -0
  20. package/dist/react-native/components/snap-separator.js +23 -0
  21. package/dist/react-native/components/snap-slider.d.ts +2 -0
  22. package/dist/react-native/components/snap-slider.js +42 -0
  23. package/dist/react-native/components/snap-stack.d.ts +5 -0
  24. package/dist/react-native/components/snap-stack.js +49 -0
  25. package/dist/react-native/components/snap-switch.d.ts +2 -0
  26. package/dist/react-native/components/snap-switch.js +30 -0
  27. package/dist/react-native/components/snap-text.d.ts +2 -0
  28. package/dist/react-native/components/snap-text.js +37 -0
  29. package/dist/react-native/components/snap-toggle-group.d.ts +2 -0
  30. package/dist/react-native/components/snap-toggle-group.js +100 -0
  31. package/dist/react-native/index.d.ts +52 -0
  32. package/dist/react-native/index.js +155 -0
  33. package/dist/react-native/theme.d.ts +21 -0
  34. package/dist/react-native/theme.js +37 -0
  35. package/dist/react-native/use-snap-palette.d.ts +13 -0
  36. package/dist/react-native/use-snap-palette.js +48 -0
  37. package/dist/ui/badge.d.ts +2 -2
  38. package/dist/ui/button.d.ts +2 -2
  39. package/dist/ui/catalog.d.ts +7 -7
  40. package/dist/ui/icon.d.ts +2 -2
  41. package/dist/ui/schema.d.ts +1 -1
  42. package/package.json +7 -2
  43. package/src/react-native/catalog-renderer.tsx +37 -0
  44. package/src/react-native/components/snap-action-button.tsx +92 -0
  45. package/src/react-native/components/snap-badge.tsx +57 -0
  46. package/src/react-native/components/snap-icon.tsx +102 -0
  47. package/src/react-native/components/snap-image.tsx +38 -0
  48. package/src/react-native/components/snap-input.tsx +57 -0
  49. package/src/react-native/components/snap-item-group.tsx +43 -0
  50. package/src/react-native/components/snap-item.tsx +70 -0
  51. package/src/react-native/components/snap-progress.tsx +40 -0
  52. package/src/react-native/components/snap-separator.tsx +32 -0
  53. package/src/react-native/components/snap-slider.tsx +82 -0
  54. package/src/react-native/components/snap-stack.tsx +66 -0
  55. package/src/react-native/components/snap-switch.tsx +45 -0
  56. package/src/react-native/components/snap-text.tsx +53 -0
  57. package/src/react-native/components/snap-toggle-group.tsx +128 -0
  58. package/src/react-native/index.tsx +267 -0
  59. package/src/react-native/theme.tsx +73 -0
  60. package/src/react-native/use-snap-palette.ts +64 -0
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Maps snap json-render catalog types to React Native primitives.
3
+ * Keys match the snap wire-format `type` strings exactly (snake_case).
4
+ */
5
+ export declare const SnapCatalogView: ComponentType<import("@json-render/react-native").CreateRendererProps>;
@@ -0,0 +1,36 @@
1
+ import { createRenderer } from "@json-render/react-native";
2
+ import { snapJsonRenderCatalog } from "@farcaster/snap/ui";
3
+ import { SnapActionButton } from "./components/snap-action-button.js";
4
+ import { SnapBadge } from "./components/snap-badge.js";
5
+ import { SnapIcon } from "./components/snap-icon.js";
6
+ import { SnapImage } from "./components/snap-image.js";
7
+ import { SnapInput } from "./components/snap-input.js";
8
+ import { SnapItem } from "./components/snap-item.js";
9
+ import { SnapItemGroup } from "./components/snap-item-group.js";
10
+ import { SnapProgress } from "./components/snap-progress.js";
11
+ import { SnapSeparator } from "./components/snap-separator.js";
12
+ import { SnapSlider } from "./components/snap-slider.js";
13
+ import { SnapStack } from "./components/snap-stack.js";
14
+ import { SnapSwitch } from "./components/snap-switch.js";
15
+ import { SnapText } from "./components/snap-text.js";
16
+ import { SnapToggleGroup } from "./components/snap-toggle-group.js";
17
+ /**
18
+ * Maps snap json-render catalog types to React Native primitives.
19
+ * Keys match the snap wire-format `type` strings exactly (snake_case).
20
+ */
21
+ export const SnapCatalogView = createRenderer(snapJsonRenderCatalog, {
22
+ badge: SnapBadge,
23
+ button: SnapActionButton,
24
+ icon: SnapIcon,
25
+ image: SnapImage,
26
+ input: SnapInput,
27
+ item: SnapItem,
28
+ item_group: SnapItemGroup,
29
+ progress: SnapProgress,
30
+ separator: SnapSeparator,
31
+ slider: SnapSlider,
32
+ stack: SnapStack,
33
+ switch: SnapSwitch,
34
+ text: SnapText,
35
+ toggle_group: SnapToggleGroup,
36
+ });
@@ -0,0 +1,2 @@
1
+ import type { ComponentRenderProps } from "@json-render/react-native";
2
+ export declare function SnapActionButton({ element: { props }, emit, }: ComponentRenderProps<Record<string, unknown>>): import("react").JSX.Element;
@@ -0,0 +1,68 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Pressable, StyleSheet, Text, View } from "react-native";
3
+ import { useSnapPalette } from "../use-snap-palette.js";
4
+ import { useSnapTheme } from "../theme.js";
5
+ import { ICON_MAP } from "./snap-icon.js";
6
+ const VARIANT_MAP = {
7
+ default: "default",
8
+ secondary: "secondary",
9
+ outline: "outline",
10
+ ghost: "ghost",
11
+ };
12
+ export function SnapActionButton({ element: { props }, emit, }) {
13
+ const { accentHex } = useSnapPalette();
14
+ const { colors } = useSnapTheme();
15
+ const label = String(props.label ?? "Action");
16
+ const variant = VARIANT_MAP[String(props.variant ?? "default")] ?? "default";
17
+ const iconName = props.icon ? String(props.icon) : undefined;
18
+ const variantStyle = (() => {
19
+ switch (variant) {
20
+ case "default":
21
+ return { backgroundColor: accentHex };
22
+ case "secondary":
23
+ return { backgroundColor: "transparent", borderWidth: 1.5, borderColor: accentHex };
24
+ case "outline":
25
+ return { backgroundColor: "rgba(255,255,255,0.04)", borderWidth: 1, borderColor: colors.border };
26
+ case "ghost":
27
+ return { backgroundColor: "transparent" };
28
+ }
29
+ })();
30
+ const textColor = variant === "default" ? "#fff" : variant === "secondary" ? accentHex : colors.text;
31
+ const iconColor = variant === "default" ? "#fff" : variant === "secondary" ? accentHex : colors.text;
32
+ return (_jsx(View, { style: styles.outer, children: _jsxs(Pressable, { style: ({ pressed }) => [
33
+ styles.btn,
34
+ variant === "default" ? styles.btnDefault : styles.btnOther,
35
+ variantStyle,
36
+ pressed && styles.pressed,
37
+ ], onPress: () => {
38
+ void (async () => {
39
+ try {
40
+ await emit("press");
41
+ }
42
+ catch (err) {
43
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
44
+ // eslint-disable-next-line no-console
45
+ console.error("[snap] action failed", err);
46
+ }
47
+ }
48
+ })();
49
+ }, children: [iconName && ICON_MAP[iconName] ? ((() => { const I = ICON_MAP[iconName]; return _jsx(I, { size: 16, color: iconColor }); })()) : null, _jsx(Text, { style: { color: textColor, fontSize: 14, fontWeight: "600" }, children: label })] }) }));
50
+ }
51
+ const styles = StyleSheet.create({
52
+ outer: { flex: 1, minWidth: 0 },
53
+ btn: {
54
+ paddingHorizontal: 16,
55
+ borderRadius: 10,
56
+ alignItems: "center",
57
+ justifyContent: "center",
58
+ flexDirection: "row",
59
+ gap: 8,
60
+ },
61
+ btnDefault: {
62
+ paddingVertical: 10,
63
+ },
64
+ btnOther: {
65
+ paddingVertical: 8,
66
+ },
67
+ pressed: { opacity: 0.88 },
68
+ });
@@ -0,0 +1,2 @@
1
+ import type { ComponentRenderProps } from "@json-render/react-native";
2
+ export declare function SnapBadge({ element: { props }, }: ComponentRenderProps<Record<string, unknown>>): import("react").JSX.Element;
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { StyleSheet, Text, View } from "react-native";
3
+ import { useSnapPalette } from "../use-snap-palette.js";
4
+ import { ICON_MAP } from "./snap-icon.js";
5
+ export function SnapBadge({ element: { props }, }) {
6
+ const { accentHex, hex } = useSnapPalette();
7
+ const label = String(props.label ?? "");
8
+ const color = props.color ? String(props.color) : undefined;
9
+ const iconName = props.icon ? String(props.icon) : undefined;
10
+ const isAccent = !color || color === "accent";
11
+ const resolvedColor = isAccent ? accentHex : hex(color);
12
+ const Icon = iconName ? ICON_MAP[iconName] : undefined;
13
+ return (_jsxs(View, { style: [
14
+ styles.badge,
15
+ isAccent
16
+ ? { backgroundColor: resolvedColor, borderColor: resolvedColor }
17
+ : { borderColor: resolvedColor },
18
+ ], children: [Icon && (_jsx(Icon, { size: 12, color: isAccent ? "#fff" : resolvedColor })), _jsx(Text, { style: [
19
+ styles.label,
20
+ { color: isAccent ? "#fff" : resolvedColor },
21
+ ], children: label })] }));
22
+ }
23
+ const styles = StyleSheet.create({
24
+ badge: {
25
+ alignSelf: "flex-start",
26
+ flexDirection: "row",
27
+ alignItems: "center",
28
+ gap: 4,
29
+ paddingHorizontal: 8,
30
+ paddingVertical: 2,
31
+ borderRadius: 9999,
32
+ borderWidth: 1,
33
+ },
34
+ label: {
35
+ fontSize: 12,
36
+ fontWeight: "500",
37
+ },
38
+ });
@@ -0,0 +1,5 @@
1
+ import type { ComponentRenderProps } from "@json-render/react-native";
2
+ import { type LucideIcon } from "lucide-react-native";
3
+ declare const ICON_MAP: Record<string, LucideIcon>;
4
+ export declare function SnapIcon({ element: { props }, }: ComponentRenderProps<Record<string, unknown>>): import("react").JSX.Element;
5
+ export { ICON_MAP };
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { View } from "react-native";
3
+ import { useSnapPalette } from "../use-snap-palette.js";
4
+ import { ArrowRight, ArrowLeft, ExternalLink, ChevronRight, Check, X, AlertTriangle, Info, Clock, Heart, MessageCircle, Repeat, Share, User, Users, Star, Trophy, Zap, Flame, Gift, ImageIcon, Play, Pause, Wallet, Coins, Plus, Minus, RefreshCw, Bookmark, ThumbsUp, ThumbsDown, TrendingUp, TrendingDown, } from "lucide-react-native";
5
+ const ICON_MAP = {
6
+ "arrow-right": ArrowRight,
7
+ "arrow-left": ArrowLeft,
8
+ "external-link": ExternalLink,
9
+ "chevron-right": ChevronRight,
10
+ check: Check,
11
+ x: X,
12
+ "alert-triangle": AlertTriangle,
13
+ info: Info,
14
+ clock: Clock,
15
+ heart: Heart,
16
+ "message-circle": MessageCircle,
17
+ repeat: Repeat,
18
+ share: Share,
19
+ user: User,
20
+ users: Users,
21
+ star: Star,
22
+ trophy: Trophy,
23
+ zap: Zap,
24
+ flame: Flame,
25
+ gift: Gift,
26
+ image: ImageIcon,
27
+ play: Play,
28
+ pause: Pause,
29
+ wallet: Wallet,
30
+ coins: Coins,
31
+ plus: Plus,
32
+ minus: Minus,
33
+ "refresh-cw": RefreshCw,
34
+ bookmark: Bookmark,
35
+ "thumbs-up": ThumbsUp,
36
+ "thumbs-down": ThumbsDown,
37
+ "trending-up": TrendingUp,
38
+ "trending-down": TrendingDown,
39
+ };
40
+ const SIZE_PX = {
41
+ sm: 16,
42
+ md: 20,
43
+ };
44
+ export function SnapIcon({ element: { props }, }) {
45
+ const { accentHex, hex } = useSnapPalette();
46
+ const name = String(props.name ?? "info");
47
+ const size = SIZE_PX[String(props.size ?? "md")] ?? 20;
48
+ const color = props.color ? String(props.color) : undefined;
49
+ const isAccent = !color || color === "accent";
50
+ const resolvedColor = isAccent ? accentHex : hex(color);
51
+ const Icon = ICON_MAP[name];
52
+ if (!Icon)
53
+ return null;
54
+ return (_jsx(View, { style: { alignItems: "center", justifyContent: "center" }, children: _jsx(Icon, { size: size, color: resolvedColor }) }));
55
+ }
56
+ export { ICON_MAP };
@@ -0,0 +1,2 @@
1
+ import type { ComponentRenderProps } from "@json-render/react-native";
2
+ export declare function SnapImage({ element: { props }, }: ComponentRenderProps<Record<string, unknown>>): import("react").JSX.Element;
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Image } from "expo-image";
3
+ import { StyleSheet, View } from "react-native";
4
+ function aspectToRatio(aspect) {
5
+ const [w, h] = aspect.split(":").map(Number);
6
+ if (!w || !h)
7
+ return 1;
8
+ return w / h;
9
+ }
10
+ export function SnapImage({ element: { props }, }) {
11
+ const url = String(props.url ?? "");
12
+ const alt = String(props.alt ?? "");
13
+ const ratio = aspectToRatio(String(props.aspect ?? "1:1"));
14
+ return (_jsx(View, { style: [styles.frame, { aspectRatio: ratio }], children: _jsx(Image, { source: { uri: url }, style: StyleSheet.absoluteFill, contentFit: "cover", accessibilityLabel: alt || undefined }) }));
15
+ }
16
+ const styles = StyleSheet.create({
17
+ frame: {
18
+ flex: 1,
19
+ width: "100%",
20
+ borderRadius: 8,
21
+ overflow: "hidden",
22
+ backgroundColor: "#f3f4f6",
23
+ },
24
+ });
@@ -0,0 +1,2 @@
1
+ import type { ComponentRenderProps } from "@json-render/react-native";
2
+ export declare function SnapInput({ element: { props }, }: ComponentRenderProps<Record<string, unknown>>): import("react").JSX.Element;
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useStateStore } from "@json-render/react-native";
3
+ import { StyleSheet, Text, TextInput, View } from "react-native";
4
+ import { useSnapTheme } from "../theme.js";
5
+ export function SnapInput({ element: { props }, }) {
6
+ const { get, set } = useStateStore();
7
+ const { colors } = useSnapTheme();
8
+ const name = String(props.name ?? "input");
9
+ const path = `/inputs/${name}`;
10
+ const label = props.label ? String(props.label) : undefined;
11
+ const placeholder = props.placeholder ? String(props.placeholder) : undefined;
12
+ const type = String(props.type ?? "text");
13
+ const maxLength = typeof props.maxLength === "number" ? props.maxLength : undefined;
14
+ const defaultValue = props.defaultValue != null ? String(props.defaultValue) : "";
15
+ const raw = get(path);
16
+ const value = raw !== undefined && raw !== null ? String(raw) : defaultValue;
17
+ return (_jsxs(View, { style: styles.wrap, children: [label ? _jsx(Text, { style: [styles.label, { color: colors.text }], children: label }) : null, _jsx(TextInput, { style: [
18
+ styles.input,
19
+ {
20
+ borderColor: colors.border,
21
+ backgroundColor: colors.inputBg,
22
+ color: colors.text,
23
+ },
24
+ ], value: value, onChangeText: (text) => set(path, type === "number" ? Number(text) || 0 : text), placeholder: placeholder, placeholderTextColor: colors.textSecondary, maxLength: maxLength, autoCapitalize: "none", autoCorrect: false, keyboardType: type === "number" ? "numeric" : "default" })] }));
25
+ }
26
+ const styles = StyleSheet.create({
27
+ wrap: { width: "100%", gap: 4 },
28
+ label: { fontSize: 13, fontWeight: "500" },
29
+ input: {
30
+ borderWidth: 1,
31
+ borderRadius: 8,
32
+ paddingHorizontal: 12,
33
+ paddingVertical: 10,
34
+ fontSize: 14,
35
+ },
36
+ });
@@ -0,0 +1,5 @@
1
+ import type { ComponentRenderProps } from "@json-render/react-native";
2
+ import { type ReactNode } from "react";
3
+ export declare function SnapItemGroup({ element: { props }, children, }: ComponentRenderProps<Record<string, unknown>> & {
4
+ children?: ReactNode;
5
+ }): import("react").JSX.Element;
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Children, Fragment } from "react";
3
+ import { StyleSheet, View } from "react-native";
4
+ import { useSnapTheme } from "../theme.js";
5
+ const GAP_MAP = { none: 0, sm: 4, md: 8, lg: 12 };
6
+ export function SnapItemGroup({ element: { props }, children, }) {
7
+ const { colors } = useSnapTheme();
8
+ const border = Boolean(props.border);
9
+ const separator = Boolean(props.separator);
10
+ const gap = GAP_MAP[String(props.gap ?? "sm")] ?? 4;
11
+ const items = Children.toArray(children);
12
+ return (_jsx(View, { style: [
13
+ styles.group,
14
+ border && { borderWidth: 1, borderColor: colors.border, borderRadius: 12 },
15
+ { gap },
16
+ ], children: items.map((child, i) => (_jsxs(Fragment, { children: [separator && i > 0 && (_jsx(View, { style: { height: 1, backgroundColor: colors.border + "80" } })), child] }, i))) }));
17
+ }
18
+ const styles = StyleSheet.create({
19
+ group: {
20
+ width: "100%",
21
+ overflow: "hidden",
22
+ },
23
+ });
@@ -0,0 +1,5 @@
1
+ import type { ComponentRenderProps } from "@json-render/react-native";
2
+ import type { ReactNode } from "react";
3
+ export declare function SnapItem({ element: { props }, children, }: ComponentRenderProps<Record<string, unknown>> & {
4
+ children?: ReactNode;
5
+ }): import("react").JSX.Element;
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { StyleSheet, Text, View } from "react-native";
3
+ import { useSnapTheme } from "../theme.js";
4
+ export function SnapItem({ element: { props }, children, }) {
5
+ const { colors } = useSnapTheme();
6
+ const title = String(props.title ?? "");
7
+ const description = props.description
8
+ ? String(props.description)
9
+ : undefined;
10
+ const variant = String(props.variant ?? "default");
11
+ const containerVariant = variant === "outline"
12
+ ? { borderWidth: 1, borderColor: colors.border + "80", borderRadius: 8, padding: 10 }
13
+ : variant === "muted"
14
+ ? { backgroundColor: "rgba(255,255,255,0.04)", borderRadius: 8, padding: 10 }
15
+ : { paddingVertical: 8, paddingHorizontal: 10 };
16
+ return (_jsxs(View, { style: [styles.container, containerVariant], children: [_jsxs(View, { style: styles.content, children: [_jsx(Text, { style: [styles.title, { color: colors.text }], children: title }), description ? (_jsx(Text, { style: [styles.description, { color: colors.textSecondary }], children: description })) : null] }), children ? (_jsx(View, { style: styles.actions, children: _jsx(View, { style: { flex: 0 }, children: children }) })) : null] }));
17
+ }
18
+ const styles = StyleSheet.create({
19
+ container: {
20
+ flex: 1,
21
+ flexDirection: "row",
22
+ alignItems: "center",
23
+ },
24
+ content: {
25
+ flex: 1,
26
+ },
27
+ title: {
28
+ fontSize: 15,
29
+ fontWeight: "500",
30
+ },
31
+ description: {
32
+ fontSize: 13,
33
+ marginTop: 1,
34
+ },
35
+ actions: {
36
+ marginLeft: "auto",
37
+ paddingLeft: 12,
38
+ flexDirection: "row",
39
+ alignItems: "center",
40
+ flexShrink: 0,
41
+ flexGrow: 0,
42
+ flexBasis: "auto",
43
+ gap: 4,
44
+ },
45
+ });
@@ -0,0 +1,2 @@
1
+ import type { ComponentRenderProps } from "@json-render/react-native";
2
+ export declare function SnapProgress({ element: { props }, }: ComponentRenderProps<Record<string, unknown>>): import("react").JSX.Element;
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { StyleSheet, Text, View } from "react-native";
3
+ import { useSnapPalette } from "../use-snap-palette.js";
4
+ import { useSnapTheme } from "../theme.js";
5
+ export function SnapProgress({ element: { props }, }) {
6
+ const { accentHex } = useSnapPalette();
7
+ const { colors } = useSnapTheme();
8
+ const value = Number(props.value ?? 0);
9
+ const max = Math.max(1, Number(props.max ?? 100));
10
+ const percent = Math.min(100, Math.max(0, (value / max) * 100));
11
+ const label = props.label != null ? String(props.label) : null;
12
+ return (_jsxs(View, { style: styles.wrap, children: [label ? (_jsx(Text, { style: [styles.label, { color: colors.textSecondary }], children: label })) : null, _jsx(View, { style: [styles.track, { backgroundColor: colors.muted }], children: _jsx(View, { style: [styles.fill, { width: `${percent}%`, backgroundColor: accentHex }] }) })] }));
13
+ }
14
+ const styles = StyleSheet.create({
15
+ wrap: { flex: 1, width: "100%", gap: 4 },
16
+ label: { fontSize: 13 },
17
+ track: {
18
+ height: 10,
19
+ borderRadius: 9999,
20
+ overflow: "hidden",
21
+ },
22
+ fill: {
23
+ height: "100%",
24
+ borderRadius: 9999,
25
+ },
26
+ });
@@ -0,0 +1,2 @@
1
+ import type { ComponentRenderProps } from "@json-render/react-native";
2
+ export declare function SnapSeparator({ element: { props }, }: ComponentRenderProps<Record<string, unknown>>): import("react").JSX.Element;
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { StyleSheet, View } from "react-native";
3
+ import { useSnapTheme } from "../theme.js";
4
+ export function SnapSeparator({ element: { props }, }) {
5
+ const { colors } = useSnapTheme();
6
+ const orientation = String(props.orientation ?? "horizontal");
7
+ const isVertical = orientation === "vertical";
8
+ return (_jsx(View, { style: [
9
+ isVertical ? styles.vertical : styles.horizontal,
10
+ { backgroundColor: colors.border + "80" },
11
+ ] }));
12
+ }
13
+ const styles = StyleSheet.create({
14
+ horizontal: {
15
+ width: "100%",
16
+ height: 1,
17
+ },
18
+ vertical: {
19
+ height: "100%",
20
+ width: 1,
21
+ alignSelf: "stretch",
22
+ },
23
+ });
@@ -0,0 +1,2 @@
1
+ import type { ComponentRenderProps } from "@json-render/react-native";
2
+ export declare function SnapSlider({ element: { props }, }: ComponentRenderProps<Record<string, unknown>>): import("react").JSX.Element;
@@ -0,0 +1,42 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useStateStore } from "@json-render/react-native";
3
+ import Slider from "@react-native-community/slider";
4
+ import { StyleSheet, Text, View } from "react-native";
5
+ import { useSnapPalette } from "../use-snap-palette.js";
6
+ import { useSnapTheme } from "../theme.js";
7
+ export function SnapSlider({ element: { props }, }) {
8
+ const { get, set } = useStateStore();
9
+ const { accentHex } = useSnapPalette();
10
+ const { colors } = useSnapTheme();
11
+ const name = String(props.name ?? "slider");
12
+ const path = `/inputs/${name}`;
13
+ const min = Number(props.min ?? 0);
14
+ const max = Number(props.max ?? 100);
15
+ const step = props.step != null ? Number(props.step) : 1;
16
+ const fallback = props.defaultValue != null ? Number(props.defaultValue) : (min + max) / 2;
17
+ const raw = get(path);
18
+ const value = raw === undefined || raw === null ? fallback : Number(raw);
19
+ const clamped = Number.isFinite(value)
20
+ ? Math.min(max, Math.max(min, value))
21
+ : fallback;
22
+ const label = props.label != null ? String(props.label) : null;
23
+ const minLabel = props.minLabel != null ? String(props.minLabel) : null;
24
+ const maxLabel = props.maxLabel != null ? String(props.maxLabel) : null;
25
+ return (_jsxs(View, { style: styles.wrap, children: [label ? (_jsxs(View, { style: styles.labelRow, children: [_jsx(Text, { style: [styles.label, { color: colors.text }], children: label }), _jsx(Text, { style: [styles.valueText, { color: colors.textSecondary }], children: String(Math.round(clamped)) })] })) : null, _jsx(Slider, { style: styles.slider, minimumValue: min, maximumValue: max, step: step > 0 ? step : 1, value: clamped, onValueChange: (v) => set(path, v), minimumTrackTintColor: accentHex, maximumTrackTintColor: colors.muted, thumbTintColor: accentHex }), minLabel != null || maxLabel != null ? (_jsxs(View, { style: styles.minMaxRow, children: [_jsx(Text, { style: [styles.minMax, { color: colors.textSecondary }], children: minLabel ?? String(min) }), _jsx(Text, { style: [styles.minMax, { color: colors.textSecondary }], children: maxLabel ?? String(max) })] })) : null] }));
26
+ }
27
+ const styles = StyleSheet.create({
28
+ wrap: { width: "100%", gap: 6 },
29
+ labelRow: {
30
+ flexDirection: "row",
31
+ justifyContent: "space-between",
32
+ alignItems: "center",
33
+ },
34
+ label: { fontSize: 13, fontWeight: "500", flex: 1 },
35
+ valueText: { fontSize: 13 },
36
+ slider: { width: "100%", height: 40 },
37
+ minMaxRow: {
38
+ flexDirection: "row",
39
+ justifyContent: "space-between",
40
+ },
41
+ minMax: { fontSize: 12 },
42
+ });
@@ -0,0 +1,5 @@
1
+ import type { ComponentRenderProps } from "@json-render/react-native";
2
+ import type { ReactNode } from "react";
3
+ export declare function SnapStack({ element: { props }, children, }: ComponentRenderProps<Record<string, unknown>> & {
4
+ children?: ReactNode;
5
+ }): import("react").JSX.Element;
@@ -0,0 +1,49 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { StyleSheet, View } from "react-native";
3
+ const VGAP = {
4
+ none: 0,
5
+ sm: 8,
6
+ md: 16,
7
+ lg: 24,
8
+ };
9
+ const HGAP = {
10
+ none: 0,
11
+ sm: 4,
12
+ md: 8,
13
+ lg: 12,
14
+ };
15
+ const JUSTIFY = {
16
+ start: "flex-start",
17
+ center: "center",
18
+ end: "flex-end",
19
+ between: "space-between",
20
+ around: "space-around",
21
+ };
22
+ export function SnapStack({ element: { props }, children, }) {
23
+ const direction = String(props.direction ?? "vertical");
24
+ const rawGap = props.gap;
25
+ const isHorizontal = direction === "horizontal";
26
+ const gapMap = isHorizontal ? HGAP : VGAP;
27
+ const gap = typeof rawGap === "number"
28
+ ? rawGap
29
+ : typeof rawGap === "string" && rawGap in gapMap
30
+ ? gapMap[rawGap]
31
+ : isHorizontal ? HGAP.md : VGAP.md;
32
+ const justify = props.justify ? JUSTIFY[String(props.justify)] : undefined;
33
+ return (_jsx(View, { style: [
34
+ styles.stack,
35
+ isHorizontal ? styles.horizontal : undefined,
36
+ { gap },
37
+ justify ? { justifyContent: justify } : undefined,
38
+ ], children: children }));
39
+ }
40
+ const styles = StyleSheet.create({
41
+ stack: {
42
+ width: "100%",
43
+ },
44
+ horizontal: {
45
+ flexDirection: "row",
46
+ alignItems: "center",
47
+ flexWrap: "wrap",
48
+ },
49
+ });
@@ -0,0 +1,2 @@
1
+ import type { ComponentRenderProps } from "@json-render/react-native";
2
+ export declare function SnapSwitch({ element: { props }, }: ComponentRenderProps<Record<string, unknown>>): import("react").JSX.Element;
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useStateStore } from "@json-render/react-native";
3
+ import { StyleSheet, Switch, Text, View } from "react-native";
4
+ import { useSnapPalette } from "../use-snap-palette.js";
5
+ import { useSnapTheme } from "../theme.js";
6
+ export function SnapSwitch({ element: { props }, }) {
7
+ const { get, set } = useStateStore();
8
+ const { accentHex } = useSnapPalette();
9
+ const { colors } = useSnapTheme();
10
+ const name = String(props.name ?? "switch");
11
+ const path = `/inputs/${name}`;
12
+ const label = props.label ? String(props.label) : undefined;
13
+ const fallback = Boolean(props.defaultChecked ?? false);
14
+ const raw = get(path);
15
+ const checked = raw === undefined || raw === null ? fallback : Boolean(raw);
16
+ return (_jsxs(View, { style: styles.row, children: [label ? _jsx(Text, { style: [styles.label, { color: colors.text }], children: label }) : null, _jsx(Switch, { value: checked, onValueChange: (v) => set(path, v), trackColor: { false: colors.border, true: accentHex }, thumbColor: "#fff" })] }));
17
+ }
18
+ const styles = StyleSheet.create({
19
+ row: {
20
+ flexDirection: "row",
21
+ alignItems: "center",
22
+ justifyContent: "space-between",
23
+ gap: 12,
24
+ },
25
+ label: {
26
+ fontSize: 14,
27
+ fontWeight: "400",
28
+ flex: 1,
29
+ },
30
+ });
@@ -0,0 +1,2 @@
1
+ import type { ComponentRenderProps } from "@json-render/react-native";
2
+ export declare function SnapText({ element: { props }, }: ComponentRenderProps<Record<string, unknown>>): import("react").JSX.Element;
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { StyleSheet, Text, View } from "react-native";
3
+ import { useSnapTheme } from "../theme.js";
4
+ const SIZE_STYLES = {
5
+ lg: { fontSize: 20, fontWeight: "700" },
6
+ md: { fontSize: 16, lineHeight: 24 },
7
+ sm: { fontSize: 13 },
8
+ };
9
+ const WEIGHT_MAP = {
10
+ bold: "700",
11
+ medium: "500",
12
+ normal: "400",
13
+ };
14
+ export function SnapText({ element: { props }, }) {
15
+ const { colors } = useSnapTheme();
16
+ const content = String(props.content ?? "");
17
+ const size = String(props.size ?? "md");
18
+ const weight = props.weight ? String(props.weight) : undefined;
19
+ const align = props.align ?? undefined;
20
+ const sizeStyle = SIZE_STYLES[size] ?? SIZE_STYLES.md;
21
+ const resolvedWeight = weight ? WEIGHT_MAP[weight] : sizeStyle?.fontWeight;
22
+ const textAlign = align === "center" ? "center" : align === "right" ? "right" : "left";
23
+ return (_jsx(View, { style: styles.wrap, children: _jsx(Text, { style: [
24
+ styles.base,
25
+ {
26
+ color: colors.text,
27
+ fontSize: sizeStyle.fontSize,
28
+ lineHeight: sizeStyle.lineHeight,
29
+ fontWeight: resolvedWeight,
30
+ textAlign,
31
+ },
32
+ ], children: content }) }));
33
+ }
34
+ const styles = StyleSheet.create({
35
+ wrap: { flex: 1, width: "100%" },
36
+ base: {},
37
+ });
@@ -0,0 +1,2 @@
1
+ import type { ComponentRenderProps } from "@json-render/react-native";
2
+ export declare function SnapToggleGroup({ element: { props }, }: ComponentRenderProps<Record<string, unknown>>): import("react").JSX.Element;