@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
|
@@ -11,6 +11,7 @@ export function SnapSlider({ element: { props }, }) {
|
|
|
11
11
|
const max = Number(props.max ?? 100);
|
|
12
12
|
const step = Number(props.step ?? 1);
|
|
13
13
|
const label = props.label ? String(props.label) : undefined;
|
|
14
|
+
const showValue = props.showValue === true;
|
|
14
15
|
const path = `/inputs/${name}`;
|
|
15
16
|
const raw = get(path);
|
|
16
17
|
const value = raw !== undefined
|
|
@@ -18,7 +19,7 @@ export function SnapSlider({ element: { props }, }) {
|
|
|
18
19
|
: props.defaultValue !== undefined
|
|
19
20
|
? Number(props.defaultValue)
|
|
20
21
|
: (min + max) / 2;
|
|
21
|
-
return (_jsxs("div", { className: "flex w-full flex-col gap-1.5", children: [label && _jsx(Label, { style: { color: colors.text }, children: label }), _jsx("input", { type: "range", min: min, max: max, step: step, value: value, onChange: (e) => set(path, Number(e.target.value)), className: "w-full h-2.5 rounded-full appearance-none cursor-pointer", style: {
|
|
22
|
+
return (_jsxs("div", { className: "flex w-full flex-col gap-1.5", children: [label && (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { style: { color: colors.text }, children: label }), showValue && (_jsx("span", { style: { color: colors.textMuted, fontSize: 13, lineHeight: "18px" }, children: Math.round(value) }))] })), _jsx("input", { type: "range", min: min, max: max, step: step, value: value, onChange: (e) => set(path, Number(e.target.value)), className: "w-full h-2.5 rounded-full appearance-none cursor-pointer", style: {
|
|
22
23
|
backgroundColor: colors.muted,
|
|
23
24
|
accentColor: colors.accent,
|
|
24
25
|
} })] }));
|
package/dist/react/index.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export type SnapActionHandlers = {
|
|
|
41
41
|
buyToken?: string;
|
|
42
42
|
}) => void;
|
|
43
43
|
};
|
|
44
|
-
export declare function SnapCard({ snap, handlers, loading, appearance, maxWidth, showOverflowWarning, onValidationError, validationErrorFallback, actionError, }: {
|
|
44
|
+
export declare function SnapCard({ snap, handlers, loading, appearance, maxWidth, showOverflowWarning, onValidationError, validationErrorFallback, actionError, plain, }: {
|
|
45
45
|
snap: SnapPage;
|
|
46
46
|
handlers: SnapActionHandlers;
|
|
47
47
|
loading?: boolean;
|
|
@@ -53,4 +53,6 @@ export declare function SnapCard({ snap, handlers, loading, appearance, maxWidth
|
|
|
53
53
|
validationErrorFallback?: ReactNode;
|
|
54
54
|
/** Server-side action error message to display inline. */
|
|
55
55
|
actionError?: string | null;
|
|
56
|
+
/** When true, renders without card frame (no border, background, or padding). */
|
|
57
|
+
plain?: boolean;
|
|
56
58
|
}): import("react/jsx-runtime").JSX.Element;
|
package/dist/react/index.js
CHANGED
|
@@ -4,9 +4,9 @@ import { SPEC_VERSION_2 } from "../constants.js";
|
|
|
4
4
|
import { SnapCardV1 } from "./v1/snap-view.js";
|
|
5
5
|
import { SnapCardV2 } from "./v2/snap-view.js";
|
|
6
6
|
// ─── SnapCard ────────────────────────────────────────
|
|
7
|
-
export function SnapCard({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, }) {
|
|
7
|
+
export function SnapCard({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, plain = false, }) {
|
|
8
8
|
if (snap.version === SPEC_VERSION_2) {
|
|
9
|
-
return (_jsx(SnapCardV2, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, maxWidth: maxWidth, showOverflowWarning: showOverflowWarning, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback, actionError: actionError }));
|
|
9
|
+
return (_jsx(SnapCardV2, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, maxWidth: maxWidth, showOverflowWarning: showOverflowWarning, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback, actionError: actionError, plain: plain }));
|
|
10
10
|
}
|
|
11
|
-
return (_jsx(SnapCardV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, maxWidth: maxWidth, actionError: actionError }));
|
|
11
|
+
return (_jsx(SnapCardV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, maxWidth: maxWidth, actionError: actionError, plain: plain }));
|
|
12
12
|
}
|
|
@@ -5,11 +5,12 @@ export declare function SnapViewV1({ snap, handlers, loading, appearance, }: {
|
|
|
5
5
|
loading?: boolean;
|
|
6
6
|
appearance?: "light" | "dark";
|
|
7
7
|
}): import("react/jsx-runtime").JSX.Element;
|
|
8
|
-
export declare function SnapCardV1({ snap, handlers, loading, appearance, maxWidth, actionError, }: {
|
|
8
|
+
export declare function SnapCardV1({ snap, handlers, loading, appearance, maxWidth, actionError, plain, }: {
|
|
9
9
|
snap: SnapPage;
|
|
10
10
|
handlers: SnapActionHandlers;
|
|
11
11
|
loading?: boolean;
|
|
12
12
|
appearance?: "light" | "dark";
|
|
13
13
|
maxWidth?: number;
|
|
14
14
|
actionError?: string | null;
|
|
15
|
+
plain?: boolean;
|
|
15
16
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,11 +1,86 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
4
|
import { SnapViewCore } from "../snap-view-core.js";
|
|
5
|
+
const SNAP_MAX_HEIGHT = 500;
|
|
4
6
|
export function SnapViewV1({ snap, handlers, loading = false, appearance = "dark", }) {
|
|
5
7
|
return (_jsx(SnapViewCore, { snap: snap, handlers: handlers, loading: loading, appearance: appearance }));
|
|
6
8
|
}
|
|
7
|
-
export function SnapCardV1({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, actionError, }) {
|
|
8
|
-
|
|
9
|
+
export function SnapCardV1({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, actionError, plain = false, }) {
|
|
10
|
+
const isDark = appearance === "dark";
|
|
11
|
+
const borderColor = isDark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.1)";
|
|
12
|
+
const surfaceBg = isDark ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.02)";
|
|
13
|
+
const toggleBg = isDark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.05)";
|
|
14
|
+
const toggleBgHover = isDark
|
|
15
|
+
? "rgba(255,255,255,0.1)"
|
|
16
|
+
: "rgba(0,0,0,0.08)";
|
|
17
|
+
const toggleText = isDark ? "rgba(255,255,255,0.82)" : "rgba(0,0,0,0.72)";
|
|
18
|
+
const contentRef = useRef(null);
|
|
19
|
+
const [isExpandable, setIsExpandable] = useState(false);
|
|
20
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
setIsExpanded(false);
|
|
23
|
+
}, [snap]);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const node = contentRef.current;
|
|
26
|
+
if (!node)
|
|
27
|
+
return;
|
|
28
|
+
const measure = () => {
|
|
29
|
+
setIsExpandable(node.scrollHeight > SNAP_MAX_HEIGHT + 1);
|
|
30
|
+
};
|
|
31
|
+
measure();
|
|
32
|
+
if (typeof ResizeObserver === "undefined")
|
|
33
|
+
return;
|
|
34
|
+
const observer = new ResizeObserver(() => {
|
|
35
|
+
measure();
|
|
36
|
+
});
|
|
37
|
+
observer.observe(node);
|
|
38
|
+
return () => observer.disconnect();
|
|
39
|
+
}, [snap, plain]);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!isExpandable) {
|
|
42
|
+
setIsExpanded(false);
|
|
43
|
+
}
|
|
44
|
+
}, [isExpandable]);
|
|
45
|
+
const isClipped = isExpandable && !isExpanded;
|
|
46
|
+
return (_jsxs("div", { style: {
|
|
47
|
+
position: "relative",
|
|
48
|
+
width: "100%",
|
|
49
|
+
maxWidth,
|
|
50
|
+
overflow: "hidden",
|
|
51
|
+
...(plain ? {} : {
|
|
52
|
+
borderRadius: 16,
|
|
53
|
+
border: `1px solid ${borderColor}`,
|
|
54
|
+
backgroundColor: surfaceBg,
|
|
55
|
+
}),
|
|
56
|
+
}, children: [_jsx("div", { style: isClipped
|
|
57
|
+
? {
|
|
58
|
+
maxHeight: SNAP_MAX_HEIGHT,
|
|
59
|
+
overflow: "hidden",
|
|
60
|
+
}
|
|
61
|
+
: undefined, children: _jsx("div", { ref: contentRef, style: plain ? undefined : { padding: 16 }, children: _jsx(SnapViewV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance }) }) }), isExpandable ? (_jsx("div", { style: {
|
|
62
|
+
display: "flex",
|
|
63
|
+
justifyContent: "center",
|
|
64
|
+
padding: plain ? "8px 0 0" : "10px 16px 12px",
|
|
65
|
+
...(plain
|
|
66
|
+
? {}
|
|
67
|
+
: { borderTop: `1px solid ${borderColor}` }),
|
|
68
|
+
}, children: _jsx("button", { type: "button", "aria-expanded": isExpanded, onClick: () => setIsExpanded((value) => !value), style: {
|
|
69
|
+
appearance: "none",
|
|
70
|
+
border: "none",
|
|
71
|
+
borderRadius: 9999,
|
|
72
|
+
backgroundColor: toggleBg,
|
|
73
|
+
color: toggleText,
|
|
74
|
+
padding: "6px 10px",
|
|
75
|
+
fontSize: 13,
|
|
76
|
+
lineHeight: "18px",
|
|
77
|
+
fontWeight: 600,
|
|
78
|
+
cursor: "pointer",
|
|
79
|
+
}, onMouseEnter: (event) => {
|
|
80
|
+
event.currentTarget.style.backgroundColor = toggleBgHover;
|
|
81
|
+
}, onMouseLeave: (event) => {
|
|
82
|
+
event.currentTarget.style.backgroundColor = toggleBg;
|
|
83
|
+
}, children: isExpanded ? "Show less" : "Show more" }) })) : null, actionError && (_jsx("div", { style: {
|
|
9
84
|
padding: "8px 12px",
|
|
10
85
|
fontSize: 13,
|
|
11
86
|
color: appearance === "dark"
|
|
@@ -9,7 +9,7 @@ export declare function SnapViewV2({ snap, handlers, loading, appearance, onVali
|
|
|
9
9
|
onValidationError?: (result: ValidationResult) => void;
|
|
10
10
|
validationErrorFallback?: ReactNode;
|
|
11
11
|
}): import("react/jsx-runtime").JSX.Element | null;
|
|
12
|
-
export declare function SnapCardV2({ snap, handlers, loading, appearance, maxWidth, showOverflowWarning, onValidationError, validationErrorFallback, actionError, }: {
|
|
12
|
+
export declare function SnapCardV2({ snap, handlers, loading, appearance, maxWidth, showOverflowWarning, onValidationError, validationErrorFallback, actionError, plain, }: {
|
|
13
13
|
snap: SnapPage;
|
|
14
14
|
handlers: SnapActionHandlers;
|
|
15
15
|
loading?: boolean;
|
|
@@ -19,4 +19,5 @@ export declare function SnapCardV2({ snap, handlers, loading, appearance, maxWid
|
|
|
19
19
|
onValidationError?: (result: ValidationResult) => void;
|
|
20
20
|
validationErrorFallback?: ReactNode;
|
|
21
21
|
actionError?: string | null;
|
|
22
|
+
plain?: boolean;
|
|
22
23
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -42,16 +42,24 @@ export function SnapViewV2({ snap, handlers, loading = false, appearance = "dark
|
|
|
42
42
|
return (_jsx(SnapViewCore, { snap: snap, handlers: handlers, loading: loading, appearance: appearance }));
|
|
43
43
|
}
|
|
44
44
|
// ─── SnapCardV2 ──────────────────────────────────────
|
|
45
|
-
export function SnapCardV2({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, }) {
|
|
45
|
+
export function SnapCardV2({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, plain = false, }) {
|
|
46
46
|
const maxHeight = showOverflowWarning ? SNAP_WARNING_HEIGHT : SNAP_MAX_HEIGHT;
|
|
47
|
-
const
|
|
47
|
+
const isDark = appearance === "dark";
|
|
48
|
+
const bg = isDark ? "rgba(0,0,0,0.85)" : "rgba(255,255,255,0.9)";
|
|
49
|
+
const borderColor = isDark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.1)";
|
|
50
|
+
const surfaceBg = isDark ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.02)";
|
|
48
51
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { style: {
|
|
49
52
|
position: "relative",
|
|
50
53
|
width: "100%",
|
|
51
54
|
maxWidth,
|
|
52
55
|
maxHeight,
|
|
53
56
|
overflow: "hidden",
|
|
54
|
-
|
|
57
|
+
...(plain ? {} : {
|
|
58
|
+
borderRadius: 16,
|
|
59
|
+
border: `1px solid ${borderColor}`,
|
|
60
|
+
backgroundColor: surfaceBg,
|
|
61
|
+
}),
|
|
62
|
+
}, children: [_jsx("div", { style: plain ? undefined : { padding: 16 }, children: _jsx(SnapViewV2, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback }) }), showOverflowWarning && (_jsxs("div", { style: {
|
|
55
63
|
position: "absolute",
|
|
56
64
|
top: SNAP_MAX_HEIGHT,
|
|
57
65
|
left: 0,
|
|
@@ -47,10 +47,10 @@ export function SnapActionButton({ element, emit, }) {
|
|
|
47
47
|
const I = ICON_MAP[iconName];
|
|
48
48
|
return _jsx(I, { size: 16, color: iconColor });
|
|
49
49
|
})()
|
|
50
|
-
: null, _jsx(Text, { style: { color: textColor, fontSize: 14, fontWeight: "600" }, children: label }), showExternalIcon ? (_jsx(ExternalLink, { size: 14, color: iconColor, style: { opacity: 0.6 } })) : null] }) }));
|
|
50
|
+
: null, _jsx(Text, { style: { color: textColor, fontSize: 14, lineHeight: 18, fontWeight: "600" }, children: label }), showExternalIcon ? (_jsx(ExternalLink, { size: 14, color: iconColor, style: { opacity: 0.6 } })) : null] }) }));
|
|
51
51
|
}
|
|
52
52
|
const styles = StyleSheet.create({
|
|
53
|
-
outer: {
|
|
53
|
+
outer: { minWidth: 0 },
|
|
54
54
|
btn: {
|
|
55
55
|
paddingHorizontal: 16,
|
|
56
56
|
borderRadius: 10,
|
|
@@ -10,7 +10,7 @@ export function SnapBadge({ element: { props }, }) {
|
|
|
10
10
|
const iconName = props.icon ? String(props.icon) : undefined;
|
|
11
11
|
const isAccent = !color || color === "accent";
|
|
12
12
|
const resolvedColor = isAccent ? accentHex : hex(color);
|
|
13
|
-
const isFilled = variant
|
|
13
|
+
const isFilled = variant !== "outline";
|
|
14
14
|
const Icon = iconName ? ICON_MAP[iconName] : undefined;
|
|
15
15
|
return (_jsxs(View, { style: [
|
|
16
16
|
styles.badge,
|
|
@@ -35,6 +35,7 @@ const styles = StyleSheet.create({
|
|
|
35
35
|
},
|
|
36
36
|
label: {
|
|
37
37
|
fontSize: 12,
|
|
38
|
+
lineHeight: 16,
|
|
38
39
|
fontWeight: "500",
|
|
39
40
|
},
|
|
40
41
|
});
|
|
@@ -30,10 +30,10 @@ export function SnapBarChart({ element: { props }, }) {
|
|
|
30
30
|
}) }));
|
|
31
31
|
}
|
|
32
32
|
const styles = StyleSheet.create({
|
|
33
|
-
wrap: {
|
|
33
|
+
wrap: { width: "100%", gap: 8 },
|
|
34
34
|
row: { flexDirection: "row", alignItems: "center", gap: 8 },
|
|
35
|
-
label: { width: 80, fontSize: 12, textAlign: "right" },
|
|
35
|
+
label: { width: 80, fontSize: 12, lineHeight: 16, textAlign: "right" },
|
|
36
36
|
track: { flex: 1, height: 10, borderRadius: 9999, overflow: "hidden" },
|
|
37
37
|
fill: { height: "100%", borderRadius: 9999 },
|
|
38
|
-
value: { width: 32, fontSize: 12, fontVariant: ["tabular-nums"] },
|
|
38
|
+
value: { width: 32, fontSize: 12, lineHeight: 16, fontVariant: ["tabular-nums"] },
|
|
39
39
|
});
|
|
@@ -91,6 +91,6 @@ const styles = StyleSheet.create({
|
|
|
91
91
|
alignItems: "center",
|
|
92
92
|
justifyContent: "center",
|
|
93
93
|
},
|
|
94
|
-
cellText: { fontSize: 12, fontWeight: "600" },
|
|
94
|
+
cellText: { fontSize: 12, lineHeight: 16, fontWeight: "600" },
|
|
95
95
|
selectionText: { fontSize: 11, fontFamily: "monospace", marginTop: 6 },
|
|
96
96
|
});
|
|
@@ -25,12 +25,13 @@ export function SnapInput({ element: { props }, }) {
|
|
|
25
25
|
}
|
|
26
26
|
const styles = StyleSheet.create({
|
|
27
27
|
wrap: { width: "100%", gap: 4 },
|
|
28
|
-
label: { fontSize: 13, fontWeight: "500" },
|
|
28
|
+
label: { fontSize: 13, lineHeight: 18, fontWeight: "500" },
|
|
29
29
|
input: {
|
|
30
30
|
borderWidth: 1,
|
|
31
31
|
borderRadius: 8,
|
|
32
32
|
paddingHorizontal: 12,
|
|
33
33
|
paddingVertical: 10,
|
|
34
34
|
fontSize: 14,
|
|
35
|
+
lineHeight: 18,
|
|
35
36
|
},
|
|
36
37
|
});
|
|
@@ -8,12 +8,11 @@ export function SnapItem({ element: { props }, children, }) {
|
|
|
8
8
|
? String(props.description)
|
|
9
9
|
: undefined;
|
|
10
10
|
const variant = String(props.variant ?? "default");
|
|
11
|
-
const containerVariant = { paddingVertical:
|
|
12
|
-
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] }));
|
|
11
|
+
const containerVariant = { paddingVertical: 6, paddingHorizontal: 10 };
|
|
12
|
+
return (_jsxs(View, { style: [styles.container, containerVariant], children: [_jsxs(View, { style: styles.content, children: [title ? _jsx(Text, { style: [styles.title, { color: colors.text }], children: title }) : null, 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] }));
|
|
13
13
|
}
|
|
14
14
|
const styles = StyleSheet.create({
|
|
15
15
|
container: {
|
|
16
|
-
flex: 1,
|
|
17
16
|
flexDirection: "row",
|
|
18
17
|
alignItems: "center",
|
|
19
18
|
},
|
|
@@ -22,10 +21,12 @@ const styles = StyleSheet.create({
|
|
|
22
21
|
},
|
|
23
22
|
title: {
|
|
24
23
|
fontSize: 15,
|
|
24
|
+
lineHeight: 20,
|
|
25
25
|
fontWeight: "500",
|
|
26
26
|
},
|
|
27
27
|
description: {
|
|
28
28
|
fontSize: 13,
|
|
29
|
+
lineHeight: 18,
|
|
29
30
|
marginTop: 1,
|
|
30
31
|
},
|
|
31
32
|
actions: {
|
|
@@ -12,8 +12,8 @@ export function SnapProgress({ element: { props }, }) {
|
|
|
12
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
13
|
}
|
|
14
14
|
const styles = StyleSheet.create({
|
|
15
|
-
wrap: {
|
|
16
|
-
label: { fontSize: 13 },
|
|
15
|
+
wrap: { width: "100%", gap: 4 },
|
|
16
|
+
label: { fontSize: 13, lineHeight: 18 },
|
|
17
17
|
track: {
|
|
18
18
|
height: 10,
|
|
19
19
|
borderRadius: 9999,
|
|
@@ -20,23 +20,24 @@ export function SnapSlider({ element: { props }, }) {
|
|
|
20
20
|
? Math.min(max, Math.max(min, value))
|
|
21
21
|
: fallback;
|
|
22
22
|
const label = props.label != null ? String(props.label) : null;
|
|
23
|
+
const showValue = props.showValue === true;
|
|
23
24
|
const minLabel = props.minLabel != null ? String(props.minLabel) : null;
|
|
24
25
|
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
|
+
return (_jsxs(View, { style: styles.wrap, children: [label ? (_jsxs(View, { style: styles.labelRow, children: [_jsx(Text, { style: [styles.label, { color: colors.text }], children: label }), showValue && (_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
|
}
|
|
27
28
|
const styles = StyleSheet.create({
|
|
28
|
-
wrap: { width: "100%", gap:
|
|
29
|
+
wrap: { width: "100%", gap: 2 },
|
|
29
30
|
labelRow: {
|
|
30
31
|
flexDirection: "row",
|
|
31
32
|
justifyContent: "space-between",
|
|
32
33
|
alignItems: "center",
|
|
33
34
|
},
|
|
34
|
-
label: { fontSize: 13, fontWeight: "500", flex: 1 },
|
|
35
|
-
valueText: { fontSize: 13 },
|
|
35
|
+
label: { fontSize: 13, lineHeight: 18, fontWeight: "500", flex: 1 },
|
|
36
|
+
valueText: { fontSize: 13, lineHeight: 18 },
|
|
36
37
|
slider: { width: "100%", height: 40 },
|
|
37
38
|
minMaxRow: {
|
|
38
39
|
flexDirection: "row",
|
|
39
40
|
justifyContent: "space-between",
|
|
40
41
|
},
|
|
41
|
-
minMax: { fontSize: 12 },
|
|
42
|
+
minMax: { fontSize: 12, lineHeight: 16 },
|
|
42
43
|
});
|
|
@@ -3,7 +3,7 @@ import { StyleSheet, Text, View } from "react-native";
|
|
|
3
3
|
import { useSnapTheme } from "../theme.js";
|
|
4
4
|
const SIZE_STYLES = {
|
|
5
5
|
md: { fontSize: 16, lineHeight: 24 },
|
|
6
|
-
sm: { fontSize: 13 },
|
|
6
|
+
sm: { fontSize: 13, lineHeight: 18 },
|
|
7
7
|
};
|
|
8
8
|
const WEIGHT_MAP = {
|
|
9
9
|
bold: "700",
|
|
@@ -30,6 +30,6 @@ export function SnapText({ element: { props }, }) {
|
|
|
30
30
|
], children: content }) }));
|
|
31
31
|
}
|
|
32
32
|
const styles = StyleSheet.create({
|
|
33
|
-
wrap: {
|
|
33
|
+
wrap: { width: "100%" },
|
|
34
34
|
base: {},
|
|
35
35
|
});
|
|
@@ -69,7 +69,7 @@ export function SnapToggleGroup({ element: { props }, }) {
|
|
|
69
69
|
}
|
|
70
70
|
const styles = StyleSheet.create({
|
|
71
71
|
wrap: { width: "100%", gap: 6 },
|
|
72
|
-
label: { fontSize: 13, fontWeight: "500" },
|
|
72
|
+
label: { fontSize: 13, lineHeight: 18, fontWeight: "500" },
|
|
73
73
|
group: {
|
|
74
74
|
padding: 4,
|
|
75
75
|
borderRadius: 8,
|
|
@@ -93,6 +93,7 @@ const styles = StyleSheet.create({
|
|
|
93
93
|
},
|
|
94
94
|
optionText: {
|
|
95
95
|
fontSize: 13,
|
|
96
|
+
lineHeight: 18,
|
|
96
97
|
fontWeight: "500",
|
|
97
98
|
},
|
|
98
99
|
});
|
|
@@ -7,7 +7,7 @@ import { hexToRgba } from "./use-snap-palette.js";
|
|
|
7
7
|
export type { JsonValue, SnapPage, SnapActionHandlers } from "./types.js";
|
|
8
8
|
export { useSnapTheme, hexToRgba };
|
|
9
9
|
export type { SnapNativeColors };
|
|
10
|
-
export declare function SnapCard({ snap, handlers, loading, appearance, colors, borderRadius, showOverflowWarning, onValidationError, validationErrorFallback, actionError, }: {
|
|
10
|
+
export declare function SnapCard({ snap, handlers, loading, appearance, colors, borderRadius, showOverflowWarning, onValidationError, validationErrorFallback, actionError, plain, }: {
|
|
11
11
|
snap: SnapPage;
|
|
12
12
|
handlers: SnapActionHandlers;
|
|
13
13
|
loading?: boolean;
|
|
@@ -23,4 +23,6 @@ export declare function SnapCard({ snap, handlers, loading, appearance, colors,
|
|
|
23
23
|
validationErrorFallback?: ReactNode;
|
|
24
24
|
/** Server-side action error message to display inline. */
|
|
25
25
|
actionError?: string | null;
|
|
26
|
+
/** When true, renders without card frame (no border, background, or padding). */
|
|
27
|
+
plain?: boolean;
|
|
26
28
|
}): import("react").JSX.Element;
|
|
@@ -7,9 +7,9 @@ import { SnapCardV2 } from "./v2/snap-view.js";
|
|
|
7
7
|
// ─── Re-exports ───────────────────────────────────────
|
|
8
8
|
export { useSnapTheme, hexToRgba };
|
|
9
9
|
// ─── SnapCard (version-switching) ─────────────────────
|
|
10
|
-
export function SnapCard({ snap, handlers, loading = false, appearance = "dark", colors, borderRadius = 16, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, }) {
|
|
10
|
+
export function SnapCard({ snap, handlers, loading = false, appearance = "dark", colors, borderRadius = 16, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, plain = false, }) {
|
|
11
11
|
if (snap.version === SPEC_VERSION_2) {
|
|
12
|
-
return (_jsx(SnapCardV2, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, colors: colors, borderRadius: borderRadius, showOverflowWarning: showOverflowWarning, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback, actionError: actionError }));
|
|
12
|
+
return (_jsx(SnapCardV2, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, colors: colors, borderRadius: borderRadius, showOverflowWarning: showOverflowWarning, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback, actionError: actionError, plain: plain }));
|
|
13
13
|
}
|
|
14
|
-
return (_jsx(SnapCardV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, colors: colors, borderRadius: borderRadius, actionError: actionError }));
|
|
14
|
+
return (_jsx(SnapCardV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, colors: colors, borderRadius: borderRadius, actionError: actionError, plain: plain }));
|
|
15
15
|
}
|
|
@@ -3,26 +3,26 @@ import { createContext, useContext, useMemo } from "react";
|
|
|
3
3
|
const DEFAULT_LIGHT = {
|
|
4
4
|
bg: "#dfe3e8",
|
|
5
5
|
surface: "#ffffff",
|
|
6
|
-
text: "
|
|
7
|
-
textSecondary: "
|
|
8
|
-
border: "
|
|
9
|
-
inputBg: "rgba(0,0,0,0.
|
|
10
|
-
muted: "rgba(0,0,0,0.
|
|
11
|
-
mutedSubtle: "rgba(0,0,0,0.
|
|
12
|
-
mutedHover: "rgba(0,0,0,0.
|
|
13
|
-
mutedSelected: "rgba(0,0,0,0.
|
|
6
|
+
text: "rgba(0,0,0,0.9)",
|
|
7
|
+
textSecondary: "rgba(0,0,0,0.5)",
|
|
8
|
+
border: "rgba(0,0,0,0.1)",
|
|
9
|
+
inputBg: "rgba(0,0,0,0.06)",
|
|
10
|
+
muted: "rgba(0,0,0,0.08)",
|
|
11
|
+
mutedSubtle: "rgba(0,0,0,0.04)",
|
|
12
|
+
mutedHover: "rgba(0,0,0,0.12)",
|
|
13
|
+
mutedSelected: "rgba(0,0,0,0.16)",
|
|
14
14
|
};
|
|
15
15
|
const DEFAULT_DARK = {
|
|
16
16
|
bg: "#111318",
|
|
17
17
|
surface: "#1a1d24",
|
|
18
|
-
text: "
|
|
19
|
-
textSecondary: "
|
|
20
|
-
border: "
|
|
21
|
-
inputBg: "rgba(255,255,255,0.
|
|
22
|
-
muted: "rgba(255,255,255,0.
|
|
23
|
-
mutedSubtle: "rgba(255,255,255,0.
|
|
24
|
-
mutedHover: "rgba(255,255,255,0.
|
|
25
|
-
mutedSelected: "rgba(255,255,255,0.
|
|
18
|
+
text: "rgba(255,255,255,0.9)",
|
|
19
|
+
textSecondary: "rgba(255,255,255,0.5)",
|
|
20
|
+
border: "rgba(255,255,255,0.1)",
|
|
21
|
+
inputBg: "rgba(255,255,255,0.04)",
|
|
22
|
+
muted: "rgba(255,255,255,0.06)",
|
|
23
|
+
mutedSubtle: "rgba(255,255,255,0.03)",
|
|
24
|
+
mutedHover: "rgba(255,255,255,0.08)",
|
|
25
|
+
mutedSelected: "rgba(255,255,255,0.12)",
|
|
26
26
|
};
|
|
27
27
|
const SnapThemeContext = createContext({
|
|
28
28
|
mode: "dark",
|
|
@@ -12,7 +12,7 @@ export declare function SnapViewV1({ snap, handlers, loading, appearance, colors
|
|
|
12
12
|
appearance?: "light" | "dark";
|
|
13
13
|
colors?: Partial<SnapNativeColors>;
|
|
14
14
|
}): import("react").JSX.Element;
|
|
15
|
-
export declare function SnapCardV1({ snap, handlers, loading, appearance, colors, borderRadius, actionError, }: {
|
|
15
|
+
export declare function SnapCardV1({ snap, handlers, loading, appearance, colors, borderRadius, actionError, plain, }: {
|
|
16
16
|
snap: SnapPage;
|
|
17
17
|
handlers: SnapActionHandlers;
|
|
18
18
|
loading?: boolean;
|
|
@@ -20,4 +20,5 @@ export declare function SnapCardV1({ snap, handlers, loading, appearance, colors
|
|
|
20
20
|
colors?: Partial<SnapNativeColors>;
|
|
21
21
|
borderRadius?: number;
|
|
22
22
|
actionError?: string | null;
|
|
23
|
+
plain?: boolean;
|
|
23
24
|
}): import("react").JSX.Element;
|
|
@@ -1,25 +1,56 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
2
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { View, Text, StyleSheet, Pressable } from "react-native";
|
|
3
4
|
import { SnapThemeProvider, useSnapTheme } from "../theme.js";
|
|
4
5
|
import { SnapViewCoreInner } from "../snap-view-core.js";
|
|
5
|
-
|
|
6
|
+
const SNAP_MAX_HEIGHT = 500;
|
|
7
|
+
// ─── SnapViewV1 (no validation) ──────────────────────
|
|
6
8
|
export function SnapViewV1Inner({ snap, handlers, loading = false, }) {
|
|
7
9
|
return (_jsx(SnapViewCoreInner, { snap: snap, handlers: handlers, loading: loading }));
|
|
8
10
|
}
|
|
9
11
|
export function SnapViewV1({ snap, handlers, loading = false, appearance = "dark", colors, }) {
|
|
10
12
|
return (_jsx(SnapThemeProvider, { appearance: appearance, colors: colors, children: _jsx(SnapViewV1Inner, { snap: snap, handlers: handlers, loading: loading }) }));
|
|
11
13
|
}
|
|
12
|
-
// ─── SnapCardV1 (card frame
|
|
13
|
-
function SnapCardV1Inner({ snap, handlers, loading = false, borderRadius, actionError, appearance, }) {
|
|
14
|
+
// ─── SnapCardV1 (card frame with expandable clipping) ──
|
|
15
|
+
function SnapCardV1Inner({ snap, handlers, loading = false, borderRadius, actionError, appearance, plain, }) {
|
|
14
16
|
const { colors } = useSnapTheme();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
const [contentHeight, setContentHeight] = useState(0);
|
|
18
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
setIsExpanded(false);
|
|
21
|
+
setContentHeight(0);
|
|
22
|
+
}, [snap]);
|
|
23
|
+
const isExpandable = contentHeight > SNAP_MAX_HEIGHT + 1;
|
|
24
|
+
const isClipped = isExpandable && !isExpanded;
|
|
25
|
+
return (_jsxs(_Fragment, { children: [_jsx(View, { style: cardStyles.frameRing, children: _jsxs(View, { style: [
|
|
26
|
+
plain ? undefined : cardStyles.card,
|
|
27
|
+
plain ? undefined : {
|
|
18
28
|
borderRadius,
|
|
19
29
|
borderColor: colors.border,
|
|
20
30
|
backgroundColor: colors.surface,
|
|
21
31
|
},
|
|
22
|
-
], children: _jsx(View, { style:
|
|
32
|
+
], children: [_jsx(View, { style: isClipped ? { maxHeight: SNAP_MAX_HEIGHT, overflow: "hidden" } : undefined, children: _jsx(View, { collapsable: false, onLayout: (event) => {
|
|
33
|
+
const nextHeight = Math.round(event.nativeEvent.layout.height);
|
|
34
|
+
setContentHeight((currentHeight) => isClipped
|
|
35
|
+
? Math.max(currentHeight, nextHeight)
|
|
36
|
+
: currentHeight === nextHeight
|
|
37
|
+
? currentHeight
|
|
38
|
+
: nextHeight);
|
|
39
|
+
}, style: plain ? undefined : cardStyles.body, children: _jsx(SnapViewV1Inner, { snap: snap, handlers: handlers, loading: loading }) }) }), isExpandable ? (_jsx(View, { style: [
|
|
40
|
+
cardStyles.expandRow,
|
|
41
|
+
plain
|
|
42
|
+
? cardStyles.expandRowPlain
|
|
43
|
+
: { borderTopColor: colors.border },
|
|
44
|
+
], children: _jsx(Pressable, { style: ({ pressed }) => [
|
|
45
|
+
cardStyles.expandButton,
|
|
46
|
+
{
|
|
47
|
+
backgroundColor: pressed
|
|
48
|
+
? colors.mutedHover
|
|
49
|
+
: colors.muted,
|
|
50
|
+
},
|
|
51
|
+
], onPress: () => {
|
|
52
|
+
setIsExpanded((value) => !value);
|
|
53
|
+
}, children: _jsx(Text, { style: [cardStyles.expandButtonText, { color: colors.text }], children: isExpanded ? "Show less" : "Show more" }) }) })) : null] }) }), actionError && (_jsx(Text, { style: [
|
|
23
54
|
cardStyles.actionError,
|
|
24
55
|
{
|
|
25
56
|
color: appearance === "dark"
|
|
@@ -28,12 +59,38 @@ function SnapCardV1Inner({ snap, handlers, loading = false, borderRadius, action
|
|
|
28
59
|
},
|
|
29
60
|
], children: actionError }))] }));
|
|
30
61
|
}
|
|
31
|
-
export function SnapCardV1({ snap, handlers, loading = false, appearance = "dark", colors, borderRadius = 16, actionError, }) {
|
|
32
|
-
return (_jsx(SnapThemeProvider, { appearance: appearance, colors: colors, children: _jsx(SnapCardV1Inner, { snap: snap, handlers: handlers, loading: loading, borderRadius: borderRadius, actionError: actionError, appearance: appearance }) }));
|
|
62
|
+
export function SnapCardV1({ snap, handlers, loading = false, appearance = "dark", colors, borderRadius = 16, actionError, plain = false, }) {
|
|
63
|
+
return (_jsx(SnapThemeProvider, { appearance: appearance, colors: colors, children: _jsx(SnapCardV1Inner, { snap: snap, handlers: handlers, loading: loading, borderRadius: borderRadius, actionError: actionError, appearance: appearance, plain: plain }) }));
|
|
33
64
|
}
|
|
34
65
|
const cardStyles = StyleSheet.create({
|
|
35
66
|
frameRing: { alignSelf: "stretch" },
|
|
36
67
|
card: { overflow: "hidden", borderWidth: 1, minHeight: 120 },
|
|
37
68
|
body: { paddingHorizontal: 16, paddingVertical: 16 },
|
|
69
|
+
expandRow: {
|
|
70
|
+
alignItems: "center",
|
|
71
|
+
paddingHorizontal: 16,
|
|
72
|
+
paddingTop: 10,
|
|
73
|
+
paddingBottom: 12,
|
|
74
|
+
borderTopWidth: StyleSheet.hairlineWidth,
|
|
75
|
+
},
|
|
76
|
+
expandRowPlain: {
|
|
77
|
+
paddingHorizontal: 0,
|
|
78
|
+
paddingTop: 8,
|
|
79
|
+
paddingBottom: 0,
|
|
80
|
+
borderTopWidth: 0,
|
|
81
|
+
},
|
|
82
|
+
expandButton: {
|
|
83
|
+
minWidth: 92,
|
|
84
|
+
alignItems: "center",
|
|
85
|
+
justifyContent: "center",
|
|
86
|
+
borderRadius: 9999,
|
|
87
|
+
paddingHorizontal: 10,
|
|
88
|
+
paddingVertical: 6,
|
|
89
|
+
},
|
|
90
|
+
expandButtonText: {
|
|
91
|
+
fontSize: 13,
|
|
92
|
+
lineHeight: 18,
|
|
93
|
+
fontWeight: "600",
|
|
94
|
+
},
|
|
38
95
|
actionError: { paddingHorizontal: 12, paddingVertical: 8, fontSize: 13 },
|
|
39
96
|
});
|
|
@@ -18,7 +18,7 @@ export declare function SnapViewV2({ snap, handlers, loading, appearance, colors
|
|
|
18
18
|
onValidationError?: (result: ValidationResult) => void;
|
|
19
19
|
validationErrorFallback?: ReactNode;
|
|
20
20
|
}): import("react").JSX.Element;
|
|
21
|
-
export declare function SnapCardV2({ snap, handlers, loading, appearance, colors, borderRadius, showOverflowWarning, onValidationError, validationErrorFallback, actionError, }: {
|
|
21
|
+
export declare function SnapCardV2({ snap, handlers, loading, appearance, colors, borderRadius, showOverflowWarning, onValidationError, validationErrorFallback, actionError, plain, }: {
|
|
22
22
|
snap: SnapPage;
|
|
23
23
|
handlers: SnapActionHandlers;
|
|
24
24
|
loading?: boolean;
|
|
@@ -29,4 +29,5 @@ export declare function SnapCardV2({ snap, handlers, loading, appearance, colors
|
|
|
29
29
|
onValidationError?: (result: ValidationResult) => void;
|
|
30
30
|
validationErrorFallback?: ReactNode;
|
|
31
31
|
actionError?: string | null;
|
|
32
|
+
plain?: boolean;
|
|
32
33
|
}): import("react").JSX.Element;
|