@farcaster/snap 2.3.0 → 2.3.1
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/colors.d.ts +8 -0
- package/dist/colors.js +21 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/react/components/action-button.js +11 -1
- package/dist/react/components/cell-grid.js +5 -2
- package/dist/react/components/item.js +6 -1
- package/dist/react/components/progress.js +6 -1
- package/dist/react-native/components/snap-cell-grid.js +5 -3
- package/package.json +1 -1
- package/src/colors.ts +22 -0
- package/src/index.ts +1 -0
- package/src/react/components/action-button.tsx +14 -1
- package/src/react/components/cell-grid.tsx +5 -2
- package/src/react/components/item.tsx +10 -1
- package/src/react/components/progress.tsx +10 -1
- package/src/react-native/components/snap-cell-grid.tsx +5 -3
package/dist/colors.d.ts
CHANGED
|
@@ -34,6 +34,14 @@ export declare function resolveSnapColorHex(color: string | undefined, opts: {
|
|
|
34
34
|
accentHex: string;
|
|
35
35
|
appearance: "light" | "dark";
|
|
36
36
|
}): string;
|
|
37
|
+
/**
|
|
38
|
+
* Pick a readable text color for a given hex background.
|
|
39
|
+
*
|
|
40
|
+
* Uses WCAG relative luminance with a 0.5 threshold. Returns `rgba(...)` so
|
|
41
|
+
* callers can soften the text against the background — defaults to 0.8 alpha
|
|
42
|
+
* to let a hint of the cell color bleed through.
|
|
43
|
+
*/
|
|
44
|
+
export declare function readableTextOnHex(hex: string, alpha?: number): string;
|
|
37
45
|
/** Light-mode hex for each palette color (emulator / reference client). */
|
|
38
46
|
export declare const PALETTE_LIGHT_HEX: Record<PaletteColor, string>;
|
|
39
47
|
/** Dark-mode hex for each palette color (reference). */
|
package/dist/colors.js
CHANGED
|
@@ -54,6 +54,27 @@ export function resolveSnapColorHex(color, opts) {
|
|
|
54
54
|
}
|
|
55
55
|
return opts.accentHex;
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Pick a readable text color for a given hex background.
|
|
59
|
+
*
|
|
60
|
+
* Uses WCAG relative luminance with a 0.5 threshold. Returns `rgba(...)` so
|
|
61
|
+
* callers can soften the text against the background — defaults to 0.8 alpha
|
|
62
|
+
* to let a hint of the cell color bleed through.
|
|
63
|
+
*/
|
|
64
|
+
export function readableTextOnHex(hex, alpha = 0.8) {
|
|
65
|
+
const m = /^#([0-9a-fA-F]{6})$/.exec(hex.trim());
|
|
66
|
+
if (!m)
|
|
67
|
+
return `rgba(0,0,0,${alpha})`;
|
|
68
|
+
const n = Number.parseInt(m[1], 16);
|
|
69
|
+
const toLin = (c) => {
|
|
70
|
+
const s = c / 255;
|
|
71
|
+
return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
|
|
72
|
+
};
|
|
73
|
+
const L = 0.2126 * toLin((n >> 16) & 0xff) +
|
|
74
|
+
0.7152 * toLin((n >> 8) & 0xff) +
|
|
75
|
+
0.0722 * toLin(n & 0xff);
|
|
76
|
+
return L >= 0.5 ? `rgba(0,0,0,${alpha})` : `rgba(255,255,255,${alpha})`;
|
|
77
|
+
}
|
|
57
78
|
/** Light-mode hex for each palette color (emulator / reference client). */
|
|
58
79
|
export const PALETTE_LIGHT_HEX = {
|
|
59
80
|
gray: "#6E6A86",
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { Spec as SnapSpec, UIElement as SnapUIElement, } from "@json-render/core";
|
|
2
2
|
export { SPEC_VERSION, SPEC_VERSION_1, SPEC_VERSION_2, SUPPORTED_SPEC_VERSIONS, type SpecVersion, SNAP_PAYLOAD_HEADER, MEDIA_TYPE, EFFECT_VALUES, POST_GRID_TAP_KEY, MAX_ELEMENTS, MAX_ROOT_CHILDREN, MAX_CHILDREN, MAX_DEPTH, } from "./constants.js";
|
|
3
|
-
export { DEFAULT_THEME_ACCENT, PALETTE_COLOR, PALETTE_COLOR_ACCENT, PALETTE_COLOR_VALUES, PALETTE_LIGHT_HEX, PALETTE_DARK_HEX, isSnapHexColorString, resolveSnapColorHex, type PaletteColor, } from "./colors.js";
|
|
3
|
+
export { DEFAULT_THEME_ACCENT, PALETTE_COLOR, PALETTE_COLOR_ACCENT, PALETTE_COLOR_VALUES, PALETTE_LIGHT_HEX, PALETTE_DARK_HEX, isSnapHexColorString, readableTextOnHex, resolveSnapColorHex, type PaletteColor, } from "./colors.js";
|
|
4
4
|
export { ACTION_TYPE_GET, ACTION_TYPE_POST, snapResponseSchema, payloadSchema, getPayloadSchema, type SnapAction, type SnapGetAction, type SnapContext, type SnapResponse, type SnapHandlerResult, type SnapElementInput, type SnapSpecInput, type SnapFunction, type SnapPayload, type SnapGetPayload, } from "./schemas.js";
|
|
5
5
|
export { validateSnapResponse, type ValidationResult } from "./validator.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { SPEC_VERSION, SPEC_VERSION_1, SPEC_VERSION_2, SUPPORTED_SPEC_VERSIONS, SNAP_PAYLOAD_HEADER, MEDIA_TYPE, EFFECT_VALUES, POST_GRID_TAP_KEY, MAX_ELEMENTS, MAX_ROOT_CHILDREN, MAX_CHILDREN, MAX_DEPTH, } from "./constants.js";
|
|
2
|
-
export { DEFAULT_THEME_ACCENT, PALETTE_COLOR, PALETTE_COLOR_ACCENT, PALETTE_COLOR_VALUES, PALETTE_LIGHT_HEX, PALETTE_DARK_HEX, isSnapHexColorString, resolveSnapColorHex, } from "./colors.js";
|
|
2
|
+
export { DEFAULT_THEME_ACCENT, PALETTE_COLOR, PALETTE_COLOR_ACCENT, PALETTE_COLOR_VALUES, PALETTE_LIGHT_HEX, PALETTE_DARK_HEX, isSnapHexColorString, readableTextOnHex, resolveSnapColorHex, } from "./colors.js";
|
|
3
3
|
export { ACTION_TYPE_GET, ACTION_TYPE_POST, snapResponseSchema, payloadSchema, getPayloadSchema, } from "./schemas.js";
|
|
4
4
|
export { validateSnapResponse } from "./validator.js";
|
|
@@ -5,6 +5,7 @@ import { ExternalLink } from "lucide-react";
|
|
|
5
5
|
import { Button } from "@neynar/ui/button";
|
|
6
6
|
import { cn } from "@neynar/ui/utils";
|
|
7
7
|
import { useSnapColors } from "../hooks/use-snap-colors.js";
|
|
8
|
+
import { useSnapStackDirection } from "../stack-direction-context.js";
|
|
8
9
|
import { ICON_MAP } from "./icon.js";
|
|
9
10
|
function isExternalLinkAction(on) {
|
|
10
11
|
if (!on)
|
|
@@ -24,6 +25,7 @@ export function SnapActionButton({ element, emit, }) {
|
|
|
24
25
|
const [hovered, setHovered] = useState(false);
|
|
25
26
|
const Icon = iconName ? ICON_MAP[iconName] : undefined;
|
|
26
27
|
const showExternalIcon = isExternalLinkAction(element.on);
|
|
28
|
+
const inHorizontalStack = useSnapStackDirection() === "horizontal";
|
|
27
29
|
const style = {
|
|
28
30
|
cursor: "pointer",
|
|
29
31
|
...(isPrimary
|
|
@@ -40,5 +42,13 @@ export function SnapActionButton({ element, emit, }) {
|
|
|
40
42
|
borderColor: "transparent",
|
|
41
43
|
}),
|
|
42
44
|
};
|
|
43
|
-
return (
|
|
45
|
+
return (
|
|
46
|
+
/**
|
|
47
|
+
* In a horizontal stack, `flex-1` lets the wrapper share row width with peers.
|
|
48
|
+
* In a vertical stack, `flex-1` would silently grow the button to fill column
|
|
49
|
+
* height (1/N distribution when siblings also flex-grow); stick to `w-full`.
|
|
50
|
+
*/
|
|
51
|
+
_jsx("div", { className: inHorizontalStack
|
|
52
|
+
? "w-full min-w-0 flex-1"
|
|
53
|
+
: "w-full min-w-0", children: _jsxs(Button, { type: "button", variant: isPrimary ? "default" : "secondary", className: cn("w-full gap-2"), style: style, onClick: () => emit("press"), onPointerEnter: () => setHovered(true), onPointerLeave: () => setHovered(false), children: [Icon && _jsx(Icon, { size: 16 }), label, showExternalIcon && (_jsx(ExternalLink, { size: 14, style: { opacity: 0.6 } }))] }) }));
|
|
44
54
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { useStateStore } from "@json-render/react";
|
|
4
4
|
import { cn } from "@neynar/ui/utils";
|
|
5
|
-
import { POST_GRID_TAP_KEY } from "@farcaster/snap";
|
|
5
|
+
import { POST_GRID_TAP_KEY, readableTextOnHex } from "@farcaster/snap";
|
|
6
6
|
import { useSnapColors } from "../hooks/use-snap-colors.js";
|
|
7
7
|
export function SnapCellGrid({ element: { props, on }, emit, }) {
|
|
8
8
|
const { get, set } = useStateStore();
|
|
@@ -69,7 +69,9 @@ export function SnapCellGrid({ element: { props, on }, emit, }) {
|
|
|
69
69
|
for (let c = 0; c < cols; c++) {
|
|
70
70
|
const cell = cellMap.get(`${r},${c}`);
|
|
71
71
|
const selected = interactive && isSelected(r, c);
|
|
72
|
-
const
|
|
72
|
+
const bgHex = cell?.color ? colors.colorHex(cell.color) : null;
|
|
73
|
+
const bg = bgHex ?? emptyCellBg;
|
|
74
|
+
const textColor = bgHex ? readableTextOnHex(bgHex) : colors.text;
|
|
73
75
|
cellEls.push(_jsx("div", { role: interactive ? "button" : undefined, tabIndex: interactive ? 0 : undefined, onClick: interactive ? () => handleTap(r, c) : undefined, onKeyDown: interactive
|
|
74
76
|
? (e) => {
|
|
75
77
|
if (e.key === "Enter" || e.key === " ") {
|
|
@@ -80,6 +82,7 @@ export function SnapCellGrid({ element: { props, on }, emit, }) {
|
|
|
80
82
|
: undefined, className: cn("flex items-center justify-center rounded text-xs font-semibold", interactive ? "cursor-pointer select-none" : "cursor-default"), style: {
|
|
81
83
|
height: rowHeight,
|
|
82
84
|
background: bg,
|
|
85
|
+
color: textColor,
|
|
83
86
|
boxShadow: selected
|
|
84
87
|
? `inset 0 0 0 1px ${colors.mode === "dark" ? "#000" : "#fff"}, inset 0 0 0 2px ${colors.mode === "dark" ? "#fff" : "#000"}`
|
|
85
88
|
: undefined,
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Item, ItemContent, ItemTitle, ItemDescription, ItemActions, } from "@neynar/ui/item";
|
|
4
|
+
import { cn } from "@neynar/ui/utils";
|
|
4
5
|
import { useSnapColors } from "../hooks/use-snap-colors.js";
|
|
6
|
+
import { useSnapStackDirection } from "../stack-direction-context.js";
|
|
5
7
|
export function SnapItem({ element: { props, children: childIds }, children, }) {
|
|
6
8
|
const title = String(props.title ?? "");
|
|
7
9
|
const description = props.description ? String(props.description) : undefined;
|
|
8
10
|
const colors = useSnapColors();
|
|
9
|
-
|
|
11
|
+
const inHorizontalStack = useSnapStackDirection() === "horizontal";
|
|
12
|
+
return (_jsxs(Item, { className: cn("py-1.5 px-2.5",
|
|
13
|
+
/** Horizontal: share width with peers. Vertical: don't fill column height. */
|
|
14
|
+
inHorizontalStack && "flex-1"), children: [_jsxs(ItemContent, { className: "gap-0.5", children: [_jsx(ItemTitle, { style: { color: colors.text }, children: title }), description && (_jsx(ItemDescription, { className: "mt-0", style: { color: colors.textMuted }, children: description }))] }), childIds && childIds.length > 0 && _jsx(ItemActions, { children: children })] }));
|
|
10
15
|
}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from "@neynar/ui/utils";
|
|
3
4
|
import { useSnapColors } from "../hooks/use-snap-colors.js";
|
|
5
|
+
import { useSnapStackDirection } from "../stack-direction-context.js";
|
|
4
6
|
export function SnapProgress({ element: { props }, }) {
|
|
5
7
|
const colors = useSnapColors();
|
|
6
8
|
const value = Number(props.value ?? 0);
|
|
7
9
|
const max = Math.max(1, Number(props.max ?? 100));
|
|
8
10
|
const percent = Math.min(100, Math.max(0, (value / max) * 100));
|
|
9
11
|
const label = props.label ? String(props.label) : null;
|
|
10
|
-
|
|
12
|
+
const inHorizontalStack = useSnapStackDirection() === "horizontal";
|
|
13
|
+
return (_jsxs("div", { className: cn("flex w-full flex-col gap-1",
|
|
14
|
+
/** Horizontal: share width with peers. Vertical: don't fill column height. */
|
|
15
|
+
inHorizontalStack && "flex-1"), children: [label && (_jsx("span", { className: "text-xs", style: { color: colors.textMuted }, children: label })), _jsx("div", { className: "h-2.5 w-full overflow-hidden rounded-full", style: { backgroundColor: colors.muted }, children: _jsx("div", { className: "h-full rounded-full transition-all", style: { width: `${percent}%`, backgroundColor: colors.accent } }) })] }));
|
|
11
16
|
}
|
|
@@ -3,7 +3,7 @@ import { StyleSheet, Text, View, Pressable } from "react-native";
|
|
|
3
3
|
import { useStateStore } from "@json-render/react-native";
|
|
4
4
|
import { useSnapPalette } from "../use-snap-palette.js";
|
|
5
5
|
import { useSnapTheme } from "../theme.js";
|
|
6
|
-
import { POST_GRID_TAP_KEY } from "@farcaster/snap";
|
|
6
|
+
import { POST_GRID_TAP_KEY, readableTextOnHex } from "@farcaster/snap";
|
|
7
7
|
export function SnapCellGrid({ element, emit, }) {
|
|
8
8
|
const { props } = element;
|
|
9
9
|
const on = element.on;
|
|
@@ -75,8 +75,10 @@ export function SnapCellGrid({ element, emit, }) {
|
|
|
75
75
|
for (let c = 0; c < cols; c++) {
|
|
76
76
|
const cell = cellMap.get(`${r},${c}`);
|
|
77
77
|
const selected = interactive && isSelected(r, c);
|
|
78
|
-
const
|
|
79
|
-
const
|
|
78
|
+
const bgHex = cell?.color ? hex(cell.color) : null;
|
|
79
|
+
const bg = bgHex ?? emptyCellBg;
|
|
80
|
+
const textColor = bgHex ? readableTextOnHex(bgHex) : colors.text;
|
|
81
|
+
const cellContent = cell?.content ? (_jsx(Text, { style: [styles.cellText, { color: textColor }], children: cell.content })) : null;
|
|
80
82
|
// Two-tone ring: outer View with contrasting border, inner View with inverse border
|
|
81
83
|
const cellView = selected ? (_jsx(View, { style: [styles.cell, { height: rowHeight, borderWidth: 1, borderColor: ringOuter, borderRadius: 4 }], children: _jsx(View, { style: [
|
|
82
84
|
styles.innerCell,
|
package/package.json
CHANGED
package/src/colors.ts
CHANGED
|
@@ -65,6 +65,28 @@ export function resolveSnapColorHex(
|
|
|
65
65
|
return opts.accentHex;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Pick a readable text color for a given hex background.
|
|
70
|
+
*
|
|
71
|
+
* Uses WCAG relative luminance with a 0.5 threshold. Returns `rgba(...)` so
|
|
72
|
+
* callers can soften the text against the background — defaults to 0.8 alpha
|
|
73
|
+
* to let a hint of the cell color bleed through.
|
|
74
|
+
*/
|
|
75
|
+
export function readableTextOnHex(hex: string, alpha = 0.8): string {
|
|
76
|
+
const m = /^#([0-9a-fA-F]{6})$/.exec(hex.trim());
|
|
77
|
+
if (!m) return `rgba(0,0,0,${alpha})`;
|
|
78
|
+
const n = Number.parseInt(m[1], 16);
|
|
79
|
+
const toLin = (c: number) => {
|
|
80
|
+
const s = c / 255;
|
|
81
|
+
return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
|
|
82
|
+
};
|
|
83
|
+
const L =
|
|
84
|
+
0.2126 * toLin((n >> 16) & 0xff) +
|
|
85
|
+
0.7152 * toLin((n >> 8) & 0xff) +
|
|
86
|
+
0.0722 * toLin(n & 0xff);
|
|
87
|
+
return L >= 0.5 ? `rgba(0,0,0,${alpha})` : `rgba(255,255,255,${alpha})`;
|
|
88
|
+
}
|
|
89
|
+
|
|
68
90
|
/** Light-mode hex for each palette color (emulator / reference client). */
|
|
69
91
|
export const PALETTE_LIGHT_HEX: Record<PaletteColor, string> = {
|
|
70
92
|
gray: "#6E6A86",
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { ExternalLink } from "lucide-react";
|
|
|
5
5
|
import { Button } from "@neynar/ui/button";
|
|
6
6
|
import { cn } from "@neynar/ui/utils";
|
|
7
7
|
import { useSnapColors } from "../hooks/use-snap-colors";
|
|
8
|
+
import { useSnapStackDirection } from "../stack-direction-context";
|
|
8
9
|
import { ICON_MAP } from "./icon";
|
|
9
10
|
|
|
10
11
|
function isExternalLinkAction(
|
|
@@ -38,6 +39,7 @@ export function SnapActionButton({
|
|
|
38
39
|
|
|
39
40
|
const Icon = iconName ? ICON_MAP[iconName] : undefined;
|
|
40
41
|
const showExternalIcon = isExternalLinkAction(element.on);
|
|
42
|
+
const inHorizontalStack = useSnapStackDirection() === "horizontal";
|
|
41
43
|
|
|
42
44
|
const style = {
|
|
43
45
|
cursor: "pointer" as const,
|
|
@@ -57,7 +59,18 @@ export function SnapActionButton({
|
|
|
57
59
|
};
|
|
58
60
|
|
|
59
61
|
return (
|
|
60
|
-
|
|
62
|
+
/**
|
|
63
|
+
* In a horizontal stack, `flex-1` lets the wrapper share row width with peers.
|
|
64
|
+
* In a vertical stack, `flex-1` would silently grow the button to fill column
|
|
65
|
+
* height (1/N distribution when siblings also flex-grow); stick to `w-full`.
|
|
66
|
+
*/
|
|
67
|
+
<div
|
|
68
|
+
className={
|
|
69
|
+
inHorizontalStack
|
|
70
|
+
? "w-full min-w-0 flex-1"
|
|
71
|
+
: "w-full min-w-0"
|
|
72
|
+
}
|
|
73
|
+
>
|
|
61
74
|
<Button
|
|
62
75
|
type="button"
|
|
63
76
|
variant={isPrimary ? "default" : "secondary"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import type { ReactNode } from "react";
|
|
4
4
|
import { useStateStore } from "@json-render/react";
|
|
5
5
|
import { cn } from "@neynar/ui/utils";
|
|
6
|
-
import { POST_GRID_TAP_KEY } from "@farcaster/snap";
|
|
6
|
+
import { POST_GRID_TAP_KEY, readableTextOnHex } from "@farcaster/snap";
|
|
7
7
|
import { useSnapColors } from "../hooks/use-snap-colors";
|
|
8
8
|
|
|
9
9
|
export function SnapCellGrid({
|
|
@@ -85,7 +85,9 @@ export function SnapCellGrid({
|
|
|
85
85
|
for (let c = 0; c < cols; c++) {
|
|
86
86
|
const cell = cellMap.get(`${r},${c}`);
|
|
87
87
|
const selected = interactive && isSelected(r, c);
|
|
88
|
-
const
|
|
88
|
+
const bgHex = cell?.color ? colors.colorHex(cell.color) : null;
|
|
89
|
+
const bg = bgHex ?? emptyCellBg;
|
|
90
|
+
const textColor = bgHex ? readableTextOnHex(bgHex) : colors.text;
|
|
89
91
|
|
|
90
92
|
cellEls.push(
|
|
91
93
|
<div
|
|
@@ -110,6 +112,7 @@ export function SnapCellGrid({
|
|
|
110
112
|
style={{
|
|
111
113
|
height: rowHeight,
|
|
112
114
|
background: bg,
|
|
115
|
+
color: textColor,
|
|
113
116
|
boxShadow: selected
|
|
114
117
|
? `inset 0 0 0 1px ${colors.mode === "dark" ? "#000" : "#fff"}, inset 0 0 0 2px ${colors.mode === "dark" ? "#fff" : "#000"}`
|
|
115
118
|
: undefined,
|
|
@@ -7,7 +7,9 @@ import {
|
|
|
7
7
|
ItemDescription,
|
|
8
8
|
ItemActions,
|
|
9
9
|
} from "@neynar/ui/item";
|
|
10
|
+
import { cn } from "@neynar/ui/utils";
|
|
10
11
|
import { useSnapColors } from "../hooks/use-snap-colors";
|
|
12
|
+
import { useSnapStackDirection } from "../stack-direction-context";
|
|
11
13
|
|
|
12
14
|
export function SnapItem({
|
|
13
15
|
element: { props, children: childIds },
|
|
@@ -19,9 +21,16 @@ export function SnapItem({
|
|
|
19
21
|
const title = String(props.title ?? "");
|
|
20
22
|
const description = props.description ? String(props.description) : undefined;
|
|
21
23
|
const colors = useSnapColors();
|
|
24
|
+
const inHorizontalStack = useSnapStackDirection() === "horizontal";
|
|
22
25
|
|
|
23
26
|
return (
|
|
24
|
-
<Item
|
|
27
|
+
<Item
|
|
28
|
+
className={cn(
|
|
29
|
+
"py-1.5 px-2.5",
|
|
30
|
+
/** Horizontal: share width with peers. Vertical: don't fill column height. */
|
|
31
|
+
inHorizontalStack && "flex-1",
|
|
32
|
+
)}
|
|
33
|
+
>
|
|
25
34
|
<ItemContent className="gap-0.5">
|
|
26
35
|
<ItemTitle style={{ color: colors.text }}>{title}</ItemTitle>
|
|
27
36
|
{description && (
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { cn } from "@neynar/ui/utils";
|
|
3
4
|
import { useSnapColors } from "../hooks/use-snap-colors";
|
|
5
|
+
import { useSnapStackDirection } from "../stack-direction-context";
|
|
4
6
|
|
|
5
7
|
export function SnapProgress({
|
|
6
8
|
element: { props },
|
|
@@ -12,9 +14,16 @@ export function SnapProgress({
|
|
|
12
14
|
const max = Math.max(1, Number(props.max ?? 100));
|
|
13
15
|
const percent = Math.min(100, Math.max(0, (value / max) * 100));
|
|
14
16
|
const label = props.label ? String(props.label) : null;
|
|
17
|
+
const inHorizontalStack = useSnapStackDirection() === "horizontal";
|
|
15
18
|
|
|
16
19
|
return (
|
|
17
|
-
<div
|
|
20
|
+
<div
|
|
21
|
+
className={cn(
|
|
22
|
+
"flex w-full flex-col gap-1",
|
|
23
|
+
/** Horizontal: share width with peers. Vertical: don't fill column height. */
|
|
24
|
+
inHorizontalStack && "flex-1",
|
|
25
|
+
)}
|
|
26
|
+
>
|
|
18
27
|
{label && (
|
|
19
28
|
<span className="text-xs" style={{ color: colors.textMuted }}>
|
|
20
29
|
{label}
|
|
@@ -3,7 +3,7 @@ import { StyleSheet, Text, View, Pressable } from "react-native";
|
|
|
3
3
|
import { useStateStore } from "@json-render/react-native";
|
|
4
4
|
import { useSnapPalette } from "../use-snap-palette";
|
|
5
5
|
import { useSnapTheme } from "../theme";
|
|
6
|
-
import { POST_GRID_TAP_KEY } from "@farcaster/snap";
|
|
6
|
+
import { POST_GRID_TAP_KEY, readableTextOnHex } from "@farcaster/snap";
|
|
7
7
|
|
|
8
8
|
export function SnapCellGrid({
|
|
9
9
|
element,
|
|
@@ -89,10 +89,12 @@ export function SnapCellGrid({
|
|
|
89
89
|
for (let c = 0; c < cols; c++) {
|
|
90
90
|
const cell = cellMap.get(`${r},${c}`);
|
|
91
91
|
const selected = interactive && isSelected(r, c);
|
|
92
|
-
const
|
|
92
|
+
const bgHex = cell?.color ? hex(cell.color) : null;
|
|
93
|
+
const bg = bgHex ?? emptyCellBg;
|
|
94
|
+
const textColor = bgHex ? readableTextOnHex(bgHex) : colors.text;
|
|
93
95
|
|
|
94
96
|
const cellContent = cell?.content ? (
|
|
95
|
-
<Text style={[styles.cellText, { color:
|
|
97
|
+
<Text style={[styles.cellText, { color: textColor }]}>
|
|
96
98
|
{cell.content}
|
|
97
99
|
</Text>
|
|
98
100
|
) : null;
|