@farcaster/snap 1.18.0 → 1.20.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/react/components/slider.js +2 -1
- package/dist/react/index.d.ts +3 -1
- package/dist/react/index.js +3 -3
- package/dist/react/v1/snap-view.d.ts +2 -1
- package/dist/react/v1/snap-view.js +77 -2
- package/dist/react/v2/snap-view.d.ts +2 -1
- package/dist/react/v2/snap-view.js +11 -3
- package/dist/react-native/components/snap-action-button.js +2 -2
- package/dist/react-native/components/snap-badge.js +2 -1
- package/dist/react-native/components/snap-bar-chart.js +3 -3
- package/dist/react-native/components/snap-cell-grid.js +1 -1
- package/dist/react-native/components/snap-image.js +0 -1
- package/dist/react-native/components/snap-input.js +2 -1
- package/dist/react-native/components/snap-item.js +4 -3
- package/dist/react-native/components/snap-progress.js +2 -2
- package/dist/react-native/components/snap-slider.js +6 -5
- package/dist/react-native/components/snap-switch.js +1 -0
- package/dist/react-native/components/snap-text.js +2 -2
- package/dist/react-native/components/snap-toggle-group.js +2 -1
- package/dist/react-native/index.d.ts +3 -1
- package/dist/react-native/index.js +3 -3
- package/dist/react-native/theme.js +16 -16
- package/dist/react-native/v1/snap-view.d.ts +2 -1
- package/dist/react-native/v1/snap-view.js +68 -11
- package/dist/react-native/v2/snap-view.d.ts +2 -1
- package/dist/react-native/v2/snap-view.js +25 -21
- package/dist/ui/catalog.d.ts +1 -1
- package/dist/ui/catalog.js +1 -2
- package/dist/ui/slider.d.ts +1 -0
- package/dist/ui/slider.js +2 -0
- package/llms.txt +1 -0
- package/package.json +1 -1
- package/src/react/components/slider.tsx +11 -1
- package/src/react/index.tsx +5 -0
- package/src/react/v1/snap-view.tsx +117 -7
- package/src/react/v2/snap-view.tsx +13 -1
- package/src/react-native/components/snap-action-button.tsx +2 -2
- package/src/react-native/components/snap-badge.tsx +2 -1
- package/src/react-native/components/snap-bar-chart.tsx +3 -3
- package/src/react-native/components/snap-cell-grid.tsx +1 -1
- package/src/react-native/components/snap-image.tsx +0 -1
- package/src/react-native/components/snap-input.tsx +2 -1
- package/src/react-native/components/snap-item.tsx +4 -3
- package/src/react-native/components/snap-progress.tsx +2 -2
- package/src/react-native/components/snap-slider.tsx +10 -7
- package/src/react-native/components/snap-switch.tsx +1 -0
- package/src/react-native/components/snap-text.tsx +2 -2
- package/src/react-native/components/snap-toggle-group.tsx +2 -1
- package/src/react-native/index.tsx +5 -0
- package/src/react-native/theme.tsx +16 -16
- package/src/react-native/v1/snap-view.tsx +102 -7
- package/src/react-native/v2/snap-view.tsx +52 -40
- package/src/ui/catalog.ts +1 -2
- package/src/ui/slider.ts +2 -0
|
@@ -21,27 +21,27 @@ export type SnapNativeColors = {
|
|
|
21
21
|
const DEFAULT_LIGHT: SnapNativeColors = {
|
|
22
22
|
bg: "#dfe3e8",
|
|
23
23
|
surface: "#ffffff",
|
|
24
|
-
text: "
|
|
25
|
-
textSecondary: "
|
|
26
|
-
border: "
|
|
27
|
-
inputBg: "rgba(0,0,0,0.
|
|
28
|
-
muted: "rgba(0,0,0,0.
|
|
29
|
-
mutedSubtle: "rgba(0,0,0,0.
|
|
30
|
-
mutedHover: "rgba(0,0,0,0.
|
|
31
|
-
mutedSelected: "rgba(0,0,0,0.
|
|
24
|
+
text: "rgba(0,0,0,0.9)",
|
|
25
|
+
textSecondary: "rgba(0,0,0,0.5)",
|
|
26
|
+
border: "rgba(0,0,0,0.1)",
|
|
27
|
+
inputBg: "rgba(0,0,0,0.06)",
|
|
28
|
+
muted: "rgba(0,0,0,0.08)",
|
|
29
|
+
mutedSubtle: "rgba(0,0,0,0.04)",
|
|
30
|
+
mutedHover: "rgba(0,0,0,0.12)",
|
|
31
|
+
mutedSelected: "rgba(0,0,0,0.16)",
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
const DEFAULT_DARK: SnapNativeColors = {
|
|
35
35
|
bg: "#111318",
|
|
36
36
|
surface: "#1a1d24",
|
|
37
|
-
text: "
|
|
38
|
-
textSecondary: "
|
|
39
|
-
border: "
|
|
40
|
-
inputBg: "rgba(255,255,255,0.
|
|
41
|
-
muted: "rgba(255,255,255,0.
|
|
42
|
-
mutedSubtle: "rgba(255,255,255,0.
|
|
43
|
-
mutedHover: "rgba(255,255,255,0.
|
|
44
|
-
mutedSelected: "rgba(255,255,255,0.
|
|
37
|
+
text: "rgba(255,255,255,0.9)",
|
|
38
|
+
textSecondary: "rgba(255,255,255,0.5)",
|
|
39
|
+
border: "rgba(255,255,255,0.1)",
|
|
40
|
+
inputBg: "rgba(255,255,255,0.04)",
|
|
41
|
+
muted: "rgba(255,255,255,0.06)",
|
|
42
|
+
mutedSubtle: "rgba(255,255,255,0.03)",
|
|
43
|
+
mutedHover: "rgba(255,255,255,0.08)",
|
|
44
|
+
mutedSelected: "rgba(255,255,255,0.12)",
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
// ─── Context ──────────────────────────────────────────
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { View, Text, StyleSheet, Pressable } from "react-native";
|
|
2
3
|
import { SnapThemeProvider, useSnapTheme, type SnapNativeColors } from "../theme";
|
|
3
4
|
import { SnapViewCoreInner } from "../snap-view-core";
|
|
4
5
|
import type { SnapPage, SnapActionHandlers } from "../types";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
const SNAP_MAX_HEIGHT = 500;
|
|
8
|
+
|
|
9
|
+
// ─── SnapViewV1 (no validation) ──────────────────────
|
|
7
10
|
|
|
8
11
|
export function SnapViewV1Inner({
|
|
9
12
|
snap,
|
|
@@ -39,7 +42,7 @@ export function SnapViewV1({
|
|
|
39
42
|
);
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
// ─── SnapCardV1 (card frame
|
|
45
|
+
// ─── SnapCardV1 (card frame with expandable clipping) ──
|
|
43
46
|
|
|
44
47
|
function SnapCardV1Inner({
|
|
45
48
|
snap,
|
|
@@ -48,6 +51,7 @@ function SnapCardV1Inner({
|
|
|
48
51
|
borderRadius,
|
|
49
52
|
actionError,
|
|
50
53
|
appearance,
|
|
54
|
+
plain,
|
|
51
55
|
}: {
|
|
52
56
|
snap: SnapPage;
|
|
53
57
|
handlers: SnapActionHandlers;
|
|
@@ -55,25 +59,87 @@ function SnapCardV1Inner({
|
|
|
55
59
|
borderRadius: number;
|
|
56
60
|
actionError?: string | null;
|
|
57
61
|
appearance: "light" | "dark";
|
|
62
|
+
plain: boolean;
|
|
58
63
|
}) {
|
|
59
64
|
const { colors } = useSnapTheme();
|
|
65
|
+
const [contentHeight, setContentHeight] = useState(0);
|
|
66
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
setIsExpanded(false);
|
|
70
|
+
setContentHeight(0);
|
|
71
|
+
}, [snap]);
|
|
72
|
+
|
|
73
|
+
const isExpandable = contentHeight > SNAP_MAX_HEIGHT + 1;
|
|
74
|
+
const isClipped = isExpandable && !isExpanded;
|
|
60
75
|
|
|
61
76
|
return (
|
|
62
77
|
<>
|
|
63
78
|
<View style={cardStyles.frameRing}>
|
|
64
79
|
<View
|
|
65
80
|
style={[
|
|
66
|
-
cardStyles.card,
|
|
67
|
-
{
|
|
81
|
+
plain ? undefined : cardStyles.card,
|
|
82
|
+
plain ? undefined : {
|
|
68
83
|
borderRadius,
|
|
69
84
|
borderColor: colors.border,
|
|
70
85
|
backgroundColor: colors.surface,
|
|
71
86
|
},
|
|
72
87
|
]}
|
|
73
88
|
>
|
|
74
|
-
<View
|
|
75
|
-
|
|
89
|
+
<View
|
|
90
|
+
style={isClipped ? { maxHeight: SNAP_MAX_HEIGHT, overflow: "hidden" } : undefined}
|
|
91
|
+
>
|
|
92
|
+
<View
|
|
93
|
+
collapsable={false}
|
|
94
|
+
onLayout={(event) => {
|
|
95
|
+
const nextHeight = Math.round(event.nativeEvent.layout.height);
|
|
96
|
+
setContentHeight((currentHeight) =>
|
|
97
|
+
isClipped
|
|
98
|
+
? Math.max(currentHeight, nextHeight)
|
|
99
|
+
: currentHeight === nextHeight
|
|
100
|
+
? currentHeight
|
|
101
|
+
: nextHeight,
|
|
102
|
+
);
|
|
103
|
+
}}
|
|
104
|
+
style={plain ? undefined : cardStyles.body}
|
|
105
|
+
>
|
|
106
|
+
<SnapViewV1Inner
|
|
107
|
+
snap={snap}
|
|
108
|
+
handlers={handlers}
|
|
109
|
+
loading={loading}
|
|
110
|
+
/>
|
|
111
|
+
</View>
|
|
76
112
|
</View>
|
|
113
|
+
{isExpandable ? (
|
|
114
|
+
<View
|
|
115
|
+
style={[
|
|
116
|
+
cardStyles.expandRow,
|
|
117
|
+
plain
|
|
118
|
+
? cardStyles.expandRowPlain
|
|
119
|
+
: { borderTopColor: colors.border },
|
|
120
|
+
]}
|
|
121
|
+
>
|
|
122
|
+
<Pressable
|
|
123
|
+
style={({ pressed }) => [
|
|
124
|
+
cardStyles.expandButton,
|
|
125
|
+
{
|
|
126
|
+
backgroundColor: pressed
|
|
127
|
+
? colors.mutedHover
|
|
128
|
+
: colors.muted,
|
|
129
|
+
},
|
|
130
|
+
]}
|
|
131
|
+
onPress={() => {
|
|
132
|
+
setIsExpanded((value) => !value);
|
|
133
|
+
}}
|
|
134
|
+
>
|
|
135
|
+
<Text
|
|
136
|
+
style={[cardStyles.expandButtonText, { color: colors.text }]}
|
|
137
|
+
>
|
|
138
|
+
{isExpanded ? "Show less" : "Show more"}
|
|
139
|
+
</Text>
|
|
140
|
+
</Pressable>
|
|
141
|
+
</View>
|
|
142
|
+
) : null}
|
|
77
143
|
</View>
|
|
78
144
|
</View>
|
|
79
145
|
{actionError && (
|
|
@@ -103,6 +169,7 @@ export function SnapCardV1({
|
|
|
103
169
|
colors,
|
|
104
170
|
borderRadius = 16,
|
|
105
171
|
actionError,
|
|
172
|
+
plain = false,
|
|
106
173
|
}: {
|
|
107
174
|
snap: SnapPage;
|
|
108
175
|
handlers: SnapActionHandlers;
|
|
@@ -111,6 +178,7 @@ export function SnapCardV1({
|
|
|
111
178
|
colors?: Partial<SnapNativeColors>;
|
|
112
179
|
borderRadius?: number;
|
|
113
180
|
actionError?: string | null;
|
|
181
|
+
plain?: boolean;
|
|
114
182
|
}) {
|
|
115
183
|
return (
|
|
116
184
|
<SnapThemeProvider appearance={appearance} colors={colors}>
|
|
@@ -121,6 +189,7 @@ export function SnapCardV1({
|
|
|
121
189
|
borderRadius={borderRadius}
|
|
122
190
|
actionError={actionError}
|
|
123
191
|
appearance={appearance}
|
|
192
|
+
plain={plain}
|
|
124
193
|
/>
|
|
125
194
|
</SnapThemeProvider>
|
|
126
195
|
);
|
|
@@ -130,5 +199,31 @@ const cardStyles = StyleSheet.create({
|
|
|
130
199
|
frameRing: { alignSelf: "stretch" },
|
|
131
200
|
card: { overflow: "hidden", borderWidth: 1, minHeight: 120 },
|
|
132
201
|
body: { paddingHorizontal: 16, paddingVertical: 16 },
|
|
202
|
+
expandRow: {
|
|
203
|
+
alignItems: "center",
|
|
204
|
+
paddingHorizontal: 16,
|
|
205
|
+
paddingTop: 10,
|
|
206
|
+
paddingBottom: 12,
|
|
207
|
+
borderTopWidth: StyleSheet.hairlineWidth,
|
|
208
|
+
},
|
|
209
|
+
expandRowPlain: {
|
|
210
|
+
paddingHorizontal: 0,
|
|
211
|
+
paddingTop: 8,
|
|
212
|
+
paddingBottom: 0,
|
|
213
|
+
borderTopWidth: 0,
|
|
214
|
+
},
|
|
215
|
+
expandButton: {
|
|
216
|
+
minWidth: 92,
|
|
217
|
+
alignItems: "center",
|
|
218
|
+
justifyContent: "center",
|
|
219
|
+
borderRadius: 9999,
|
|
220
|
+
paddingHorizontal: 10,
|
|
221
|
+
paddingVertical: 6,
|
|
222
|
+
},
|
|
223
|
+
expandButtonText: {
|
|
224
|
+
fontSize: 13,
|
|
225
|
+
lineHeight: 18,
|
|
226
|
+
fontWeight: "600",
|
|
227
|
+
},
|
|
133
228
|
actionError: { paddingHorizontal: 12, paddingVertical: 8, fontSize: 13 },
|
|
134
229
|
});
|
|
@@ -123,6 +123,7 @@ function SnapCardV2Inner({
|
|
|
123
123
|
validationErrorFallback,
|
|
124
124
|
actionError,
|
|
125
125
|
appearance,
|
|
126
|
+
plain,
|
|
126
127
|
}: {
|
|
127
128
|
snap: SnapPage;
|
|
128
129
|
handlers: SnapActionHandlers;
|
|
@@ -133,54 +134,62 @@ function SnapCardV2Inner({
|
|
|
133
134
|
validationErrorFallback?: ReactNode;
|
|
134
135
|
actionError?: string | null;
|
|
135
136
|
appearance: "light" | "dark";
|
|
137
|
+
plain: boolean;
|
|
136
138
|
}) {
|
|
137
139
|
const { colors } = useSnapTheme();
|
|
138
|
-
const
|
|
140
|
+
const clipHeight = showOverflowWarning ? undefined : SNAP_MAX_HEIGHT;
|
|
141
|
+
|
|
142
|
+
const content = (
|
|
143
|
+
<SnapViewV2Inner
|
|
144
|
+
snap={snap}
|
|
145
|
+
handlers={handlers}
|
|
146
|
+
loading={loading}
|
|
147
|
+
onValidationError={onValidationError}
|
|
148
|
+
validationErrorFallback={validationErrorFallback}
|
|
149
|
+
/>
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
if (plain) {
|
|
153
|
+
return content;
|
|
154
|
+
}
|
|
139
155
|
|
|
140
156
|
return (
|
|
141
157
|
<>
|
|
142
|
-
<View
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
>
|
|
154
|
-
|
|
155
|
-
<SnapViewV2Inner
|
|
156
|
-
snap={snap}
|
|
157
|
-
handlers={handlers}
|
|
158
|
-
loading={loading}
|
|
159
|
-
onValidationError={onValidationError}
|
|
160
|
-
validationErrorFallback={validationErrorFallback}
|
|
161
|
-
/>
|
|
162
|
-
</View>
|
|
163
|
-
{showOverflowWarning && (
|
|
164
|
-
<View style={cardStyles.warningOverlay}>
|
|
165
|
-
<View style={cardStyles.warningLine} />
|
|
166
|
-
<View style={cardStyles.warningLabel}>
|
|
167
|
-
<Text style={cardStyles.warningLabelText}>{SNAP_MAX_HEIGHT}px</Text>
|
|
168
|
-
</View>
|
|
169
|
-
</View>
|
|
170
|
-
)}
|
|
158
|
+
<View
|
|
159
|
+
style={{
|
|
160
|
+
borderRadius,
|
|
161
|
+
borderWidth: 1,
|
|
162
|
+
borderColor: colors.border,
|
|
163
|
+
backgroundColor: colors.surface,
|
|
164
|
+
maxHeight: clipHeight,
|
|
165
|
+
overflow: "hidden",
|
|
166
|
+
minHeight: 120,
|
|
167
|
+
}}
|
|
168
|
+
>
|
|
169
|
+
<View style={{ paddingHorizontal: 16, paddingVertical: 16 }}>
|
|
170
|
+
{content}
|
|
171
171
|
</View>
|
|
172
|
+
{showOverflowWarning && (
|
|
173
|
+
<View style={{ position: "absolute", top: SNAP_MAX_HEIGHT, left: 0, right: 0, bottom: 0, zIndex: 10, pointerEvents: "none" }}>
|
|
174
|
+
<View style={{ height: 1, borderTopWidth: 1, borderStyle: "dashed", borderColor: "rgba(255,100,100,0.6)" }} />
|
|
175
|
+
<View style={{ position: "absolute", top: -10, right: 4, backgroundColor: "rgba(0,0,0,0.7)", paddingHorizontal: 4, paddingVertical: 1, borderRadius: 3 }}>
|
|
176
|
+
<Text style={{ fontSize: 10, color: "rgba(255,100,100,0.7)", fontFamily: Platform.select({ ios: "Menlo", default: "monospace" }) }}>{SNAP_MAX_HEIGHT}px</Text>
|
|
177
|
+
</View>
|
|
178
|
+
<View style={{ flex: 1, backgroundColor: "rgba(255,50,50,0.15)" }} />
|
|
179
|
+
</View>
|
|
180
|
+
)}
|
|
172
181
|
</View>
|
|
173
182
|
{actionError && (
|
|
174
183
|
<Text
|
|
175
|
-
style={
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
+
style={{
|
|
185
|
+
paddingHorizontal: 12,
|
|
186
|
+
paddingVertical: 8,
|
|
187
|
+
fontSize: 13,
|
|
188
|
+
color:
|
|
189
|
+
appearance === "dark"
|
|
190
|
+
? "rgba(255,100,100,0.9)"
|
|
191
|
+
: "rgba(200,0,0,0.8)",
|
|
192
|
+
}}
|
|
184
193
|
>
|
|
185
194
|
{actionError}
|
|
186
195
|
</Text>
|
|
@@ -200,6 +209,7 @@ export function SnapCardV2({
|
|
|
200
209
|
onValidationError,
|
|
201
210
|
validationErrorFallback,
|
|
202
211
|
actionError,
|
|
212
|
+
plain = false,
|
|
203
213
|
}: {
|
|
204
214
|
snap: SnapPage;
|
|
205
215
|
handlers: SnapActionHandlers;
|
|
@@ -211,6 +221,7 @@ export function SnapCardV2({
|
|
|
211
221
|
onValidationError?: (result: ValidationResult) => void;
|
|
212
222
|
validationErrorFallback?: ReactNode;
|
|
213
223
|
actionError?: string | null;
|
|
224
|
+
plain?: boolean;
|
|
214
225
|
}) {
|
|
215
226
|
return (
|
|
216
227
|
<SnapThemeProvider appearance={appearance} colors={colors}>
|
|
@@ -224,6 +235,7 @@ export function SnapCardV2({
|
|
|
224
235
|
validationErrorFallback={validationErrorFallback}
|
|
225
236
|
actionError={actionError}
|
|
226
237
|
appearance={appearance}
|
|
238
|
+
plain={plain}
|
|
227
239
|
/>
|
|
228
240
|
</SnapThemeProvider>
|
|
229
241
|
);
|
|
@@ -231,7 +243,7 @@ export function SnapCardV2({
|
|
|
231
243
|
|
|
232
244
|
const cardStyles = StyleSheet.create({
|
|
233
245
|
frameRing: { alignSelf: "stretch" },
|
|
234
|
-
card: {
|
|
246
|
+
card: { borderWidth: 1, minHeight: 120, overflow: "hidden" },
|
|
235
247
|
body: { paddingHorizontal: 16, paddingVertical: 16 },
|
|
236
248
|
actionError: { paddingHorizontal: 12, paddingVertical: 8, fontSize: 13 },
|
|
237
249
|
warningOverlay: {
|
package/src/ui/catalog.ts
CHANGED
|
@@ -117,10 +117,9 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
117
117
|
params: z.object({ target: z.string() }),
|
|
118
118
|
},
|
|
119
119
|
open_url: {
|
|
120
|
-
description: "Open
|
|
120
|
+
description: "Open URL in browser.",
|
|
121
121
|
params: z.object({
|
|
122
122
|
target: z.string(),
|
|
123
|
-
isSnap: z.boolean().optional(),
|
|
124
123
|
}),
|
|
125
124
|
},
|
|
126
125
|
open_mini_app: {
|
package/src/ui/slider.ts
CHANGED
|
@@ -12,6 +12,8 @@ export const sliderProps = z
|
|
|
12
12
|
step: z.number().optional(),
|
|
13
13
|
defaultValue: z.number().optional(),
|
|
14
14
|
label: z.string().max(SLIDER_MAX_LABEL_CHARS).optional(),
|
|
15
|
+
/** When true, display the current value next to the label. */
|
|
16
|
+
showValue: z.boolean().optional(),
|
|
15
17
|
})
|
|
16
18
|
.superRefine((val, ctx) => {
|
|
17
19
|
if (val.min > val.max) {
|