@farcaster/snap 2.5.1 → 2.6.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/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/react/catalog-renderer.js +2 -0
- package/dist/react/components/action-button.js +9 -1
- package/dist/react/components/cell-grid.js +16 -2
- package/dist/react/components/image.js +5 -2
- package/dist/react/components/paginator.d.ts +7 -0
- package/dist/react/components/paginator.js +103 -0
- package/dist/react/components/stack.js +21 -18
- package/dist/react/components/text.js +13 -1
- package/dist/react/snap-version-context.d.ts +3 -0
- package/dist/react/snap-version-context.js +7 -0
- package/dist/react/snap-view-core.d.ts +1 -1
- package/dist/react/snap-view-core.js +27 -4
- package/dist/react-native/catalog-renderer.js +2 -0
- package/dist/react-native/components/snap-action-button.js +8 -2
- package/dist/react-native/components/snap-cell-grid.js +16 -2
- package/dist/react-native/components/snap-image.js +29 -4
- package/dist/react-native/components/snap-paginator.d.ts +5 -0
- package/dist/react-native/components/snap-paginator.js +194 -0
- package/dist/react-native/components/snap-text.js +4 -3
- package/dist/react-native/expand-state.d.ts +19 -0
- package/dist/react-native/expand-state.js +18 -0
- package/dist/react-native/index.d.ts +7 -1
- package/dist/react-native/index.js +3 -3
- package/dist/react-native/snap-version-context.d.ts +3 -0
- package/dist/react-native/snap-version-context.js +6 -0
- package/dist/react-native/snap-view-core.d.ts +1 -1
- package/dist/react-native/snap-view-core.js +27 -4
- package/dist/react-native/v1/snap-view.d.ts +7 -1
- package/dist/react-native/v1/snap-view.js +35 -11
- package/dist/react-native/v2/snap-view.d.ts +7 -1
- package/dist/react-native/v2/snap-view.js +60 -17
- package/dist/ui/catalog.d.ts +45 -0
- package/dist/ui/catalog.js +20 -3
- package/dist/ui/cell-grid.d.ts +5 -0
- package/dist/ui/cell-grid.js +2 -1
- package/dist/ui/image.d.ts +4 -1
- package/dist/ui/image.js +3 -1
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +1 -0
- package/dist/ui/paginator-state.d.ts +18 -0
- package/dist/ui/paginator-state.js +47 -0
- package/dist/ui/paginator.d.ts +17 -0
- package/dist/ui/paginator.js +8 -0
- package/dist/ui/text.d.ts +1 -0
- package/dist/ui/text.js +1 -0
- package/dist/validator.js +16 -3
- package/llms.txt +19 -4
- package/package.json +1 -1
- package/src/constants.ts +1 -0
- package/src/react/catalog-renderer.tsx +2 -0
- package/src/react/components/action-button.tsx +13 -2
- package/src/react/components/cell-grid.tsx +22 -2
- package/src/react/components/image.tsx +17 -0
- package/src/react/components/paginator.tsx +208 -0
- package/src/react/components/stack.tsx +20 -18
- package/src/react/components/text.tsx +13 -1
- package/src/react/snap-version-context.tsx +12 -0
- package/src/react/snap-view-core.tsx +44 -12
- package/src/react-native/catalog-renderer.tsx +2 -0
- package/src/react-native/components/snap-action-button.tsx +10 -2
- package/src/react-native/components/snap-cell-grid.tsx +22 -2
- package/src/react-native/components/snap-image.tsx +40 -1
- package/src/react-native/components/snap-paginator.tsx +283 -0
- package/src/react-native/components/snap-text.tsx +4 -2
- package/src/react-native/expand-state.ts +48 -0
- package/src/react-native/index.tsx +15 -0
- package/src/react-native/snap-version-context.tsx +10 -0
- package/src/react-native/snap-view-core.tsx +47 -12
- package/src/react-native/v1/snap-view.tsx +57 -10
- package/src/react-native/v2/snap-view.tsx +88 -17
- package/src/ui/catalog.ts +25 -3
- package/src/ui/cell-grid.ts +2 -0
- package/src/ui/image.ts +3 -1
- package/src/ui/index.ts +3 -0
- package/src/ui/paginator-state.ts +67 -0
- package/src/ui/paginator.ts +11 -0
- package/src/ui/text.ts +1 -0
- package/src/validator.ts +19 -3
|
@@ -4,8 +4,8 @@ import { Platform, Pressable, StyleSheet, Text, View } from "react-native";
|
|
|
4
4
|
import { SnapThemeProvider, useSnapTheme } from "../theme.js";
|
|
5
5
|
import { SnapLoadingOverlay, SnapViewCoreInner, resolveAccentHex, } from "../snap-view-core.js";
|
|
6
6
|
import { validateSnapResponse, } from "@farcaster/snap";
|
|
7
|
+
import { getSnapExpansionState, SNAP_MAX_HEIGHT } from "../expand-state.js";
|
|
7
8
|
// ─── Constants ───────────────────────────────────────
|
|
8
|
-
const SNAP_MAX_HEIGHT = 500;
|
|
9
9
|
const SNAP_WARNING_HEIGHT = 700;
|
|
10
10
|
const SHOW_MORE_OVERHANG = 14;
|
|
11
11
|
// ─── Validation fallback ─────────────────────────────
|
|
@@ -51,7 +51,7 @@ export function SnapViewV2({ snap, handlers, loading = false, appearance = "dark
|
|
|
51
51
|
return (_jsx(SnapThemeProvider, { appearance: appearance, colors: colors, children: _jsx(SnapViewV2Inner, { snap: snap, handlers: handlers, loading: loading, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback, loadingOverlay: loadingOverlay }) }));
|
|
52
52
|
}
|
|
53
53
|
// ─── SnapCardV2 (card frame + height limits) ─────────
|
|
54
|
-
function SnapCardV2Inner({ snap, handlers, loading, borderRadius, showOverflowWarning, onValidationError, validationErrorFallback, actionError, appearance, plain, loadingOverlay, }) {
|
|
54
|
+
function SnapCardV2Inner({ snap, handlers, loading, borderRadius, showOverflowWarning, onValidationError, validationErrorFallback, actionError, appearance, plain, loadingOverlay, forceExpanded, expandButtonLabel, onExpandPress, }) {
|
|
55
55
|
const { colors, mode } = useSnapTheme();
|
|
56
56
|
const accentHex = resolveAccentHex(snap.theme?.accent, mode);
|
|
57
57
|
const [contentHeight, setContentHeight] = useState(0);
|
|
@@ -60,13 +60,22 @@ function SnapCardV2Inner({ snap, handlers, loading, borderRadius, showOverflowWa
|
|
|
60
60
|
setIsExpanded(false);
|
|
61
61
|
setContentHeight(0);
|
|
62
62
|
}, [snap]);
|
|
63
|
-
const
|
|
64
|
-
|
|
63
|
+
const expansion = getSnapExpansionState({
|
|
64
|
+
contentHeight,
|
|
65
|
+
internalExpanded: isExpanded,
|
|
66
|
+
forceExpanded,
|
|
67
|
+
onExpandPress,
|
|
68
|
+
expandButtonLabel,
|
|
69
|
+
showOverflowWarning,
|
|
70
|
+
});
|
|
71
|
+
const expandButtonInsideCard = typeof onExpandPress === "function";
|
|
65
72
|
const content = (_jsx(SnapViewV2Inner, { snap: snap, handlers: handlers, loading: loading, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback, loadingOverlay: null }));
|
|
66
73
|
if (plain) {
|
|
67
|
-
return (_jsxs(_Fragment, { children: [_jsx(View, { style:
|
|
74
|
+
return (_jsxs(_Fragment, { children: [_jsx(View, { style: expansion.clipped
|
|
75
|
+
? { maxHeight: expansion.maxHeight, overflow: "hidden" }
|
|
76
|
+
: undefined, children: _jsx(View, { collapsable: false, onLayout: (e) => {
|
|
68
77
|
const nextHeight = Math.round(e.nativeEvent.layout.height);
|
|
69
|
-
setContentHeight((current) =>
|
|
78
|
+
setContentHeight((current) => expansion.clipped
|
|
70
79
|
? Math.max(current, nextHeight)
|
|
71
80
|
: current === nextHeight
|
|
72
81
|
? current
|
|
@@ -75,43 +84,65 @@ function SnapCardV2Inner({ snap, handlers, loading, borderRadius, showOverflowWa
|
|
|
75
84
|
? loadingOverlay === undefined
|
|
76
85
|
? _jsx(SnapLoadingOverlay, { appearance: mode, accentHex: accentHex })
|
|
77
86
|
: loadingOverlay
|
|
78
|
-
: null,
|
|
87
|
+
: null, expansion.showButton ? (_jsx(View, { style: [cardStyles.expandRow, cardStyles.expandRowPlain], children: _jsx(Pressable, { style: ({ pressed }) => [
|
|
79
88
|
cardStyles.expandButton,
|
|
80
89
|
{
|
|
81
90
|
backgroundColor: pressed ? colors.mutedHover : colors.muted,
|
|
82
91
|
},
|
|
83
|
-
], onPress: () =>
|
|
92
|
+
], onPress: () => {
|
|
93
|
+
if (expansion.useInternalToggle) {
|
|
94
|
+
setIsExpanded((value) => !value);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
onExpandPress?.();
|
|
98
|
+
}
|
|
99
|
+
}, children: _jsx(Text, { style: [cardStyles.expandButtonText, { color: colors.text }], children: expansion.buttonLabel }) }) })) : null] }));
|
|
84
100
|
}
|
|
85
|
-
const overflowAmount = showOverflowWarning
|
|
101
|
+
const overflowAmount = expansion.showOverflowWarning
|
|
102
|
+
? contentHeight - SNAP_MAX_HEIGHT
|
|
103
|
+
: 0;
|
|
86
104
|
const isDark = mode === "dark";
|
|
87
105
|
const pillBg = isDark ? "rgba(40,40,40,0.92)" : "rgba(255,255,255,0.92)";
|
|
88
106
|
const pillBgPressed = isDark ? "rgba(60,60,60,0.95)" : "rgba(240,240,240,0.95)";
|
|
89
|
-
return (_jsxs(View, { style: {
|
|
107
|
+
return (_jsxs(View, { style: {
|
|
108
|
+
paddingBottom: expansion.showButton && !expandButtonInsideCard ? SHOW_MORE_OVERHANG : 0,
|
|
109
|
+
}, children: [_jsxs(View, { style: { position: "relative" }, children: [_jsxs(View, { style: {
|
|
90
110
|
borderRadius,
|
|
91
111
|
borderWidth: 1,
|
|
92
112
|
borderColor: colors.border,
|
|
93
113
|
backgroundColor: colors.surface,
|
|
94
|
-
maxHeight: showOverflowWarning
|
|
114
|
+
maxHeight: expansion.showOverflowWarning
|
|
115
|
+
? undefined
|
|
116
|
+
: expansion.maxHeight,
|
|
95
117
|
overflow: "hidden",
|
|
96
118
|
minHeight: 120,
|
|
97
119
|
}, children: [_jsx(View, { collapsable: false, onLayout: (e) => {
|
|
98
120
|
const nextHeight = Math.round(e.nativeEvent.layout.height);
|
|
99
|
-
setContentHeight((current) =>
|
|
121
|
+
setContentHeight((current) => expansion.clipped
|
|
100
122
|
? Math.max(current, nextHeight)
|
|
101
123
|
: current === nextHeight
|
|
102
124
|
? current
|
|
103
125
|
: nextHeight);
|
|
104
|
-
}, style: { paddingHorizontal: 16, paddingVertical: 16 }, children: content }), showOverflowWarning && contentHeight > SNAP_MAX_HEIGHT && (_jsxs(View, { style: { position: "absolute", top: SNAP_MAX_HEIGHT, left: 0, right: 0, height: overflowAmount, zIndex: 10, pointerEvents: "none" }, children: [_jsx(View, { style: { height: 1, borderTopWidth: 1, borderStyle: "dashed", borderColor: "rgba(255,100,100,0.6)" } }), _jsx(View, { style: { position: "absolute", top: -10, right: 4, backgroundColor: "rgba(0,0,0,0.7)", paddingHorizontal: 4, paddingVertical: 1, borderRadius: 3 }, children: _jsxs(Text, { style: { fontSize: 10, color: "rgba(255,100,100,0.7)", fontFamily: Platform.select({ ios: "Menlo", default: "monospace" }) }, children: [SNAP_MAX_HEIGHT, "px"] }) }), _jsx(View, { style: { flex: 1, backgroundColor: "rgba(255,50,50,0.15)" } })] })), loading
|
|
126
|
+
}, style: { paddingHorizontal: 16, paddingVertical: 16 }, children: content }), expansion.showOverflowWarning && contentHeight > SNAP_MAX_HEIGHT && (_jsxs(View, { style: { position: "absolute", top: SNAP_MAX_HEIGHT, left: 0, right: 0, height: overflowAmount, zIndex: 10, pointerEvents: "none" }, children: [_jsx(View, { style: { height: 1, borderTopWidth: 1, borderStyle: "dashed", borderColor: "rgba(255,100,100,0.6)" } }), _jsx(View, { style: { position: "absolute", top: -10, right: 4, backgroundColor: "rgba(0,0,0,0.7)", paddingHorizontal: 4, paddingVertical: 1, borderRadius: 3 }, children: _jsxs(Text, { style: { fontSize: 10, color: "rgba(255,100,100,0.7)", fontFamily: Platform.select({ ios: "Menlo", default: "monospace" }) }, children: [SNAP_MAX_HEIGHT, "px"] }) }), _jsx(View, { style: { flex: 1, backgroundColor: "rgba(255,50,50,0.15)" } })] })), loading
|
|
105
127
|
? loadingOverlay === undefined
|
|
106
128
|
? _jsx(SnapLoadingOverlay, { appearance: mode, accentHex: accentHex })
|
|
107
129
|
: loadingOverlay
|
|
108
|
-
: null] }),
|
|
130
|
+
: null] }), expansion.showButton ? (_jsx(View, { pointerEvents: "box-none", style: expandButtonInsideCard
|
|
131
|
+
? cardStyles.expandFloatInset
|
|
132
|
+
: cardStyles.expandFloat, children: _jsx(Pressable, { style: ({ pressed }) => [
|
|
109
133
|
cardStyles.expandButton,
|
|
110
134
|
{
|
|
111
135
|
backgroundColor: pressed ? pillBgPressed : pillBg,
|
|
112
136
|
borderColor: colors.border,
|
|
113
137
|
},
|
|
114
|
-
], onPress: () =>
|
|
138
|
+
], onPress: () => {
|
|
139
|
+
if (expansion.useInternalToggle) {
|
|
140
|
+
setIsExpanded((value) => !value);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
onExpandPress?.();
|
|
144
|
+
}
|
|
145
|
+
}, children: _jsx(Text, { style: [cardStyles.expandButtonText, { color: colors.text }], children: expansion.buttonLabel }) }) })) : null] }), actionError && (_jsx(Text, { style: {
|
|
115
146
|
paddingHorizontal: 12,
|
|
116
147
|
paddingVertical: 8,
|
|
117
148
|
fontSize: 13,
|
|
@@ -120,8 +151,8 @@ function SnapCardV2Inner({ snap, handlers, loading, borderRadius, showOverflowWa
|
|
|
120
151
|
: "rgba(200,0,0,0.8)",
|
|
121
152
|
}, children: actionError }))] }));
|
|
122
153
|
}
|
|
123
|
-
export function SnapCardV2({ snap, handlers, loading = false, appearance = "dark", colors, borderRadius = 16, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, plain = false, loadingOverlay, }) {
|
|
124
|
-
return (_jsx(SnapThemeProvider, { appearance: appearance, colors: colors, children: _jsx(SnapCardV2Inner, { snap: snap, handlers: handlers, loading: loading, borderRadius: borderRadius, showOverflowWarning: showOverflowWarning, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback, actionError: actionError, appearance: appearance, plain: plain, loadingOverlay: loadingOverlay }) }));
|
|
154
|
+
export function SnapCardV2({ snap, handlers, loading = false, appearance = "dark", colors, borderRadius = 16, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, plain = false, loadingOverlay, forceExpanded, expandButtonLabel, onExpandPress, }) {
|
|
155
|
+
return (_jsx(SnapThemeProvider, { appearance: appearance, colors: colors, children: _jsx(SnapCardV2Inner, { snap: snap, handlers: handlers, loading: loading, borderRadius: borderRadius, showOverflowWarning: showOverflowWarning, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback, actionError: actionError, appearance: appearance, plain: plain, loadingOverlay: loadingOverlay, forceExpanded: forceExpanded, expandButtonLabel: expandButtonLabel, onExpandPress: onExpandPress }) }));
|
|
125
156
|
}
|
|
126
157
|
const cardStyles = StyleSheet.create({
|
|
127
158
|
frameRing: { alignSelf: "stretch" },
|
|
@@ -137,6 +168,18 @@ const cardStyles = StyleSheet.create({
|
|
|
137
168
|
alignItems: "center",
|
|
138
169
|
justifyContent: "center",
|
|
139
170
|
},
|
|
171
|
+
expandFloatInset: {
|
|
172
|
+
position: "absolute",
|
|
173
|
+
left: 0,
|
|
174
|
+
right: 0,
|
|
175
|
+
bottom: 10,
|
|
176
|
+
height: 28,
|
|
177
|
+
alignItems: "center",
|
|
178
|
+
justifyContent: "center",
|
|
179
|
+
},
|
|
180
|
+
expandRow: {
|
|
181
|
+
alignItems: "center",
|
|
182
|
+
},
|
|
140
183
|
expandRowPlain: {
|
|
141
184
|
paddingTop: 8,
|
|
142
185
|
alignItems: "center",
|
package/dist/ui/catalog.d.ts
CHANGED
|
@@ -319,8 +319,29 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
319
319
|
"16:9": "16:9";
|
|
320
320
|
"4:3": "4:3";
|
|
321
321
|
"9:16": "9:16";
|
|
322
|
+
"4:1": "4:1";
|
|
322
323
|
}>;
|
|
323
324
|
alt: z.ZodOptional<z.ZodString>;
|
|
325
|
+
title: z.ZodOptional<z.ZodString>;
|
|
326
|
+
subtitle: z.ZodOptional<z.ZodString>;
|
|
327
|
+
}, z.core.$strip>;
|
|
328
|
+
description: string;
|
|
329
|
+
};
|
|
330
|
+
paginator: {
|
|
331
|
+
props: z.ZodObject<{
|
|
332
|
+
initialPage: z.ZodOptional<z.ZodNumber>;
|
|
333
|
+
showIndicators: z.ZodOptional<z.ZodBoolean>;
|
|
334
|
+
showControls: z.ZodOptional<z.ZodBoolean>;
|
|
335
|
+
controlsPosition: z.ZodOptional<z.ZodEnum<{
|
|
336
|
+
top: "top";
|
|
337
|
+
bottom: "bottom";
|
|
338
|
+
}>>;
|
|
339
|
+
transition: z.ZodOptional<z.ZodEnum<{
|
|
340
|
+
none: "none";
|
|
341
|
+
slide: "slide";
|
|
342
|
+
fade: "fade";
|
|
343
|
+
scale: "scale";
|
|
344
|
+
}>>;
|
|
324
345
|
}, z.core.$strip>;
|
|
325
346
|
description: string;
|
|
326
347
|
};
|
|
@@ -393,6 +414,7 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
393
414
|
left: "left";
|
|
394
415
|
right: "right";
|
|
395
416
|
}>>;
|
|
417
|
+
maxLines: z.ZodOptional<z.ZodNumber>;
|
|
396
418
|
}, z.core.$strip>;
|
|
397
419
|
description: string;
|
|
398
420
|
};
|
|
@@ -469,6 +491,11 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
469
491
|
square: "square";
|
|
470
492
|
}>>;
|
|
471
493
|
rowHeight: z.ZodOptional<z.ZodNumber>;
|
|
494
|
+
maxWidth: z.ZodOptional<z.ZodEnum<{
|
|
495
|
+
sm: "sm";
|
|
496
|
+
md: "md";
|
|
497
|
+
lg: "lg";
|
|
498
|
+
}>>;
|
|
472
499
|
select: z.ZodOptional<z.ZodEnum<{
|
|
473
500
|
multiple: "multiple";
|
|
474
501
|
off: "off";
|
|
@@ -545,5 +572,23 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
545
572
|
buyToken: z.ZodOptional<z.ZodString>;
|
|
546
573
|
}, z.core.$strip>;
|
|
547
574
|
};
|
|
575
|
+
paginator_next: {
|
|
576
|
+
description: string;
|
|
577
|
+
params: z.ZodObject<{
|
|
578
|
+
page: z.ZodOptional<z.ZodNumber>;
|
|
579
|
+
}, z.core.$strip>;
|
|
580
|
+
};
|
|
581
|
+
paginator_prev: {
|
|
582
|
+
description: string;
|
|
583
|
+
params: z.ZodObject<{
|
|
584
|
+
page: z.ZodOptional<z.ZodNumber>;
|
|
585
|
+
}, z.core.$strip>;
|
|
586
|
+
};
|
|
587
|
+
paginator_go_to: {
|
|
588
|
+
description: string;
|
|
589
|
+
params: z.ZodObject<{
|
|
590
|
+
page: z.ZodNumber;
|
|
591
|
+
}, z.core.$strip>;
|
|
592
|
+
};
|
|
548
593
|
};
|
|
549
594
|
}>;
|
package/dist/ui/catalog.js
CHANGED
|
@@ -10,6 +10,7 @@ import { inputProps } from "./input.js";
|
|
|
10
10
|
import { itemProps } from "./item.js";
|
|
11
11
|
import { itemGroupProps } from "./item-group.js";
|
|
12
12
|
import { imageProps } from "./image.js";
|
|
13
|
+
import { paginatorProps } from "./paginator.js";
|
|
13
14
|
import { progressProps } from "./progress.js";
|
|
14
15
|
import { separatorProps } from "./separator.js";
|
|
15
16
|
import { sliderProps } from "./slider.js";
|
|
@@ -62,7 +63,11 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
62
63
|
},
|
|
63
64
|
image: {
|
|
64
65
|
props: imageProps,
|
|
65
|
-
description: "HTTPS image with fixed aspect ratio.",
|
|
66
|
+
description: "HTTPS image with fixed aspect ratio. Supports compact 4:1 banners and optional title/subtitle overlay text.",
|
|
67
|
+
},
|
|
68
|
+
paginator: {
|
|
69
|
+
props: paginatorProps,
|
|
70
|
+
description: "Client-side paginator. Children are page element ids; the @farcaster/snap React/React Native components render one page at a time with optional built-in previous/next controls and indicators, optional top/bottom controlsPosition, and author-controlled local transition (slide | fade | scale | none). Buttons or cell_grid cells in the same snap can bind paginator_next, paginator_prev, or paginator_go_to for custom local navigation. Only one paginator is supported per rendered snap in this release. Page index is json-render local UI state and is not posted as input.",
|
|
66
71
|
},
|
|
67
72
|
progress: {
|
|
68
73
|
props: progressProps,
|
|
@@ -82,7 +87,7 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
82
87
|
},
|
|
83
88
|
text: {
|
|
84
89
|
props: textProps,
|
|
85
|
-
description: "Text block — size: md (body, default), sm (caption). Optional weight and
|
|
90
|
+
description: "Text block — size: md (body, default), sm (caption). Optional weight, align, and maxLines. Text does not clamp by default; set maxLines to bound rendered lines.",
|
|
86
91
|
},
|
|
87
92
|
bar_chart: {
|
|
88
93
|
props: barChartProps,
|
|
@@ -90,7 +95,7 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
90
95
|
},
|
|
91
96
|
cell_grid: {
|
|
92
97
|
props: cellGridProps,
|
|
93
|
-
description: "Cell grid — sparse colored cells on a rows×cols grid. Cell color and textColor are palette names or literal #rrggbb hex values (hex ignores page accent); textColor overrides the default auto-contrast text color. Two interaction modes: leave select 'off' and bind on.press to fire an action per cell press (inputs[name] is the pressed 'row,col' before the action runs); or set select 'single'/'multiple' for press-to-select with a visual ring (no auto-fire — pair with a separate submit button). on.press is ignored when select is on.",
|
|
98
|
+
description: "Cell grid — sparse colored cells on a rows×cols grid. Set maxWidth to sm or md to render compact square boards centered instead of stretching full-width; lg is the default full-width behavior. Cell color and textColor are palette names or literal #rrggbb hex values (hex ignores page accent); textColor overrides the default auto-contrast text color. Two interaction modes: leave select 'off' and bind on.press to fire an action per cell press (inputs[name] is the pressed 'row,col' before the action runs); or set select 'single'/'multiple' for press-to-select with a visual ring (no auto-fire — pair with a separate submit button). on.press is ignored when select is on.",
|
|
94
99
|
},
|
|
95
100
|
},
|
|
96
101
|
actions: {
|
|
@@ -146,5 +151,17 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
146
151
|
buyToken: z.string().optional(),
|
|
147
152
|
}),
|
|
148
153
|
},
|
|
154
|
+
paginator_next: {
|
|
155
|
+
description: "Move the snap's paginator to the next page locally. Does not POST and is ignored when no paginator is rendered.",
|
|
156
|
+
params: z.object({ page: z.number().int().min(0).optional() }),
|
|
157
|
+
},
|
|
158
|
+
paginator_prev: {
|
|
159
|
+
description: "Move the snap's paginator to the previous page locally. Does not POST and is ignored when no paginator is rendered.",
|
|
160
|
+
params: z.object({ page: z.number().int().min(0).optional() }),
|
|
161
|
+
},
|
|
162
|
+
paginator_go_to: {
|
|
163
|
+
description: "Move the snap's paginator to a specific zero-based page index locally. Does not POST and is ignored when no paginator is rendered.",
|
|
164
|
+
params: z.object({ page: z.number().int().min(0) }),
|
|
165
|
+
},
|
|
149
166
|
},
|
|
150
167
|
});
|
package/dist/ui/cell-grid.d.ts
CHANGED
|
@@ -40,6 +40,11 @@ export declare const cellGridProps: z.ZodObject<{
|
|
|
40
40
|
square: "square";
|
|
41
41
|
}>>;
|
|
42
42
|
rowHeight: z.ZodOptional<z.ZodNumber>;
|
|
43
|
+
maxWidth: z.ZodOptional<z.ZodEnum<{
|
|
44
|
+
sm: "sm";
|
|
45
|
+
md: "md";
|
|
46
|
+
lg: "lg";
|
|
47
|
+
}>>;
|
|
43
48
|
select: z.ZodOptional<z.ZodEnum<{
|
|
44
49
|
multiple: "multiple";
|
|
45
50
|
off: "off";
|
package/dist/ui/cell-grid.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { isSnapHexColorString, PALETTE_COLOR_VALUES } from "../colors.js";
|
|
3
|
-
import { GRID_MIN_COLS, GRID_MAX_COLS, GRID_MIN_ROWS, GRID_MAX_ROWS, GRID_GAP_VALUES, GRID_CELL_ASPECT_RATIO_VALUES, } from "../constants.js";
|
|
3
|
+
import { GRID_MIN_COLS, GRID_MAX_COLS, GRID_MIN_ROWS, GRID_MAX_ROWS, GRID_GAP_VALUES, GRID_CELL_ASPECT_RATIO_VALUES, GRID_MAX_WIDTH_VALUES, } from "../constants.js";
|
|
4
4
|
/** Palette name or `#rrggbb`; input is trimmed so palette and hex rules match runtime resolvers. */
|
|
5
5
|
const cellGridCellColorSchema = (field) => z.preprocess((v) => (typeof v === "string" ? v.trim() : v), z.union([
|
|
6
6
|
z.enum(PALETTE_COLOR_VALUES),
|
|
@@ -25,6 +25,7 @@ export const cellGridProps = z
|
|
|
25
25
|
gap: z.enum(GRID_GAP_VALUES).optional(),
|
|
26
26
|
cellAspectRatio: z.enum(GRID_CELL_ASPECT_RATIO_VALUES).optional(),
|
|
27
27
|
rowHeight: z.number().int().min(8).max(64).optional(),
|
|
28
|
+
maxWidth: z.enum(GRID_MAX_WIDTH_VALUES).optional(),
|
|
28
29
|
select: z.enum(["off", "single", "multiple"]).optional(),
|
|
29
30
|
})
|
|
30
31
|
.superRefine((val, ctx) => {
|
package/dist/ui/image.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export declare const IMAGE_ASPECTS: readonly ["1:1", "16:9", "4:3", "9:16"];
|
|
2
|
+
export declare const IMAGE_ASPECTS: readonly ["1:1", "16:9", "4:3", "9:16", "4:1"];
|
|
3
3
|
export declare const imageProps: z.ZodObject<{
|
|
4
4
|
url: z.ZodString;
|
|
5
5
|
aspect: z.ZodEnum<{
|
|
@@ -7,7 +7,10 @@ export declare const imageProps: z.ZodObject<{
|
|
|
7
7
|
"16:9": "16:9";
|
|
8
8
|
"4:3": "4:3";
|
|
9
9
|
"9:16": "9:16";
|
|
10
|
+
"4:1": "4:1";
|
|
10
11
|
}>;
|
|
11
12
|
alt: z.ZodOptional<z.ZodString>;
|
|
13
|
+
title: z.ZodOptional<z.ZodString>;
|
|
14
|
+
subtitle: z.ZodOptional<z.ZodString>;
|
|
12
15
|
}, z.core.$strip>;
|
|
13
16
|
export type ImageProps = z.infer<typeof imageProps>;
|
package/dist/ui/image.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export const IMAGE_ASPECTS = ["1:1", "16:9", "4:3", "9:16"];
|
|
2
|
+
export const IMAGE_ASPECTS = ["1:1", "16:9", "4:3", "9:16", "4:1"];
|
|
3
3
|
export const imageProps = z.object({
|
|
4
4
|
url: z.string(),
|
|
5
5
|
aspect: z.enum(IMAGE_ASPECTS),
|
|
6
6
|
alt: z.string().optional(),
|
|
7
|
+
title: z.string().min(1).max(80).optional(),
|
|
8
|
+
subtitle: z.string().min(1).max(120).optional(),
|
|
7
9
|
});
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export { iconProps, ICON_NAMES } from "./icon.js";
|
|
|
18
18
|
export type { IconProps } from "./icon.js";
|
|
19
19
|
export { imageProps } from "./image.js";
|
|
20
20
|
export type { ImageProps } from "./image.js";
|
|
21
|
+
export { paginatorProps } from "./paginator.js";
|
|
22
|
+
export type { PaginatorProps } from "./paginator.js";
|
|
21
23
|
export { progressProps } from "./progress.js";
|
|
22
24
|
export type { ProgressProps } from "./progress.js";
|
|
23
25
|
export { separatorProps } from "./separator.js";
|
package/dist/ui/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export { itemProps, itemMediaProps } from "./item.js";
|
|
|
9
9
|
export { itemGroupProps } from "./item-group.js";
|
|
10
10
|
export { iconProps, ICON_NAMES } from "./icon.js";
|
|
11
11
|
export { imageProps } from "./image.js";
|
|
12
|
+
export { paginatorProps } from "./paginator.js";
|
|
12
13
|
export { progressProps } from "./progress.js";
|
|
13
14
|
export { separatorProps } from "./separator.js";
|
|
14
15
|
export { sliderProps } from "./slider.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const SNAP_PAGINATOR_PAGE_PATH = "/ui/paginator/page";
|
|
2
|
+
export declare const SNAP_PAGINATOR_PAGE_COUNT_PATH = "/ui/paginator/pageCount";
|
|
3
|
+
export type SnapPaginatorAction = {
|
|
4
|
+
action: "paginator_next" | "paginator_prev";
|
|
5
|
+
} | {
|
|
6
|
+
action: "paginator_go_to";
|
|
7
|
+
page?: number;
|
|
8
|
+
};
|
|
9
|
+
type StateStoreAccess = {
|
|
10
|
+
get: (path: string) => unknown;
|
|
11
|
+
set: (path: string, value: unknown) => void;
|
|
12
|
+
};
|
|
13
|
+
export declare function clampPaginatorPage(value: number, pageCount: number): number;
|
|
14
|
+
export declare function pageFromValue(value: unknown, fallback?: number): number;
|
|
15
|
+
export declare function pageCountFromValue(value: unknown): number;
|
|
16
|
+
export declare function getPaginatorAction(on: Record<string, unknown> | undefined): SnapPaginatorAction | null;
|
|
17
|
+
export declare function runPaginatorAction(store: StateStoreAccess, action: SnapPaginatorAction | null): boolean;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const SNAP_PAGINATOR_PAGE_PATH = "/ui/paginator/page";
|
|
2
|
+
export const SNAP_PAGINATOR_PAGE_COUNT_PATH = "/ui/paginator/pageCount";
|
|
3
|
+
export function clampPaginatorPage(value, pageCount) {
|
|
4
|
+
return Math.min(Math.max(value, 0), Math.max(pageCount - 1, 0));
|
|
5
|
+
}
|
|
6
|
+
export function pageFromValue(value, fallback = 0) {
|
|
7
|
+
return typeof value === "number" && Number.isInteger(value)
|
|
8
|
+
? value
|
|
9
|
+
: fallback;
|
|
10
|
+
}
|
|
11
|
+
export function pageCountFromValue(value) {
|
|
12
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0
|
|
13
|
+
? value
|
|
14
|
+
: 0;
|
|
15
|
+
}
|
|
16
|
+
export function getPaginatorAction(on) {
|
|
17
|
+
const press = on?.press;
|
|
18
|
+
if (!press)
|
|
19
|
+
return null;
|
|
20
|
+
if (press.action === "paginator_next")
|
|
21
|
+
return { action: "paginator_next" };
|
|
22
|
+
if (press.action === "paginator_prev")
|
|
23
|
+
return { action: "paginator_prev" };
|
|
24
|
+
if (press.action === "paginator_go_to") {
|
|
25
|
+
const page = press.params?.page;
|
|
26
|
+
return {
|
|
27
|
+
action: "paginator_go_to",
|
|
28
|
+
page: typeof page === "number" && Number.isInteger(page) ? page : 0,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
export function runPaginatorAction(store, action) {
|
|
34
|
+
if (!action)
|
|
35
|
+
return false;
|
|
36
|
+
const pageCount = pageCountFromValue(store.get(SNAP_PAGINATOR_PAGE_COUNT_PATH));
|
|
37
|
+
if (pageCount === 0)
|
|
38
|
+
return false;
|
|
39
|
+
const currentPage = clampPaginatorPage(pageFromValue(store.get(SNAP_PAGINATOR_PAGE_PATH), 0), pageCount);
|
|
40
|
+
const nextPage = action.action === "paginator_next"
|
|
41
|
+
? currentPage + 1
|
|
42
|
+
: action.action === "paginator_prev"
|
|
43
|
+
? currentPage - 1
|
|
44
|
+
: action.page ?? 0;
|
|
45
|
+
store.set(SNAP_PAGINATOR_PAGE_PATH, clampPaginatorPage(nextPage, pageCount));
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const paginatorProps: z.ZodObject<{
|
|
3
|
+
initialPage: z.ZodOptional<z.ZodNumber>;
|
|
4
|
+
showIndicators: z.ZodOptional<z.ZodBoolean>;
|
|
5
|
+
showControls: z.ZodOptional<z.ZodBoolean>;
|
|
6
|
+
controlsPosition: z.ZodOptional<z.ZodEnum<{
|
|
7
|
+
top: "top";
|
|
8
|
+
bottom: "bottom";
|
|
9
|
+
}>>;
|
|
10
|
+
transition: z.ZodOptional<z.ZodEnum<{
|
|
11
|
+
none: "none";
|
|
12
|
+
slide: "slide";
|
|
13
|
+
fade: "fade";
|
|
14
|
+
scale: "scale";
|
|
15
|
+
}>>;
|
|
16
|
+
}, z.core.$strip>;
|
|
17
|
+
export type PaginatorProps = z.infer<typeof paginatorProps>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const paginatorProps = z.object({
|
|
3
|
+
initialPage: z.number().int().min(0).optional(),
|
|
4
|
+
showIndicators: z.boolean().optional(),
|
|
5
|
+
showControls: z.boolean().optional(),
|
|
6
|
+
controlsPosition: z.enum(["top", "bottom"]).optional(),
|
|
7
|
+
transition: z.enum(["slide", "fade", "scale", "none"]).optional(),
|
|
8
|
+
});
|
package/dist/ui/text.d.ts
CHANGED
package/dist/ui/text.js
CHANGED
package/dist/validator.js
CHANGED
|
@@ -76,9 +76,12 @@ function validateStructure(ui) {
|
|
|
76
76
|
path: ["ui", "elements"],
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
|
-
// Root element has a stricter children limit
|
|
79
|
+
// Root element has a stricter children limit. Paginator pages are intentionally
|
|
80
|
+
// allowed to exceed per-container child caps while preserving global limits.
|
|
80
81
|
const rootEl = elements[ui.root];
|
|
81
|
-
if (rootEl?.
|
|
82
|
+
if (rootEl?.type !== "paginator" &&
|
|
83
|
+
rootEl?.children &&
|
|
84
|
+
rootEl.children.length > MAX_ROOT_CHILDREN) {
|
|
82
85
|
issues.push({
|
|
83
86
|
code: "custom",
|
|
84
87
|
message: `Root element "${ui.root}" exceeds maximum of ${MAX_ROOT_CHILDREN} children (found ${rootEl.children.length})`,
|
|
@@ -88,7 +91,7 @@ function validateStructure(ui) {
|
|
|
88
91
|
for (const [id, el] of Object.entries(elements)) {
|
|
89
92
|
if (id === ui.root)
|
|
90
93
|
continue; // already checked above
|
|
91
|
-
if (el.children && el.children.length > MAX_CHILDREN) {
|
|
94
|
+
if (el.type !== "paginator" && el.children && el.children.length > MAX_CHILDREN) {
|
|
92
95
|
issues.push({
|
|
93
96
|
code: "custom",
|
|
94
97
|
message: `Element "${id}" exceeds maximum of ${MAX_CHILDREN} children (found ${el.children.length})`,
|
|
@@ -96,6 +99,16 @@ function validateStructure(ui) {
|
|
|
96
99
|
});
|
|
97
100
|
}
|
|
98
101
|
}
|
|
102
|
+
const paginatorIds = Object.entries(elements)
|
|
103
|
+
.filter(([, el]) => el.type === "paginator")
|
|
104
|
+
.map(([id]) => id);
|
|
105
|
+
if (paginatorIds.length > 1) {
|
|
106
|
+
issues.push({
|
|
107
|
+
code: "custom",
|
|
108
|
+
message: `Snap supports at most one paginator (found ${paginatorIds.length}: ${paginatorIds.join(", ")})`,
|
|
109
|
+
path: ["ui", "elements"],
|
|
110
|
+
});
|
|
111
|
+
}
|
|
99
112
|
const depth = measureDepth(elements, ui.root);
|
|
100
113
|
if (depth > MAX_DEPTH) {
|
|
101
114
|
issues.push({
|
package/llms.txt
CHANGED
|
@@ -36,10 +36,10 @@ Top-level fields: `version` (required, `"1.0"` or `"2.0"`), `theme` (optional, `
|
|
|
36
36
|
|------------|-------|
|
|
37
37
|
| Total elements | Max **64** in `ui.elements` |
|
|
38
38
|
| Root children | Max **7** children on the root element |
|
|
39
|
-
| Children per element | Max **6** per non-root container (`stack`, `item_group`) |
|
|
39
|
+
| Children per element | Max **6** per non-root container (`stack`, `item_group`); `paginator` pages are exempt |
|
|
40
40
|
| Nesting depth | Max **5** levels from root to deepest leaf |
|
|
41
41
|
|
|
42
|
-
## Components (
|
|
42
|
+
## Components (17 total)
|
|
43
43
|
|
|
44
44
|
### Display Components
|
|
45
45
|
|
|
@@ -61,8 +61,9 @@ Top-level fields: `version` (required, `"1.0"` or `"2.0"`), `theme` (optional, `
|
|
|
61
61
|
|
|
62
62
|
**image** — HTTPS image with fixed aspect ratio.
|
|
63
63
|
- `url` (string, required)
|
|
64
|
-
- `aspect` (required): `"1:1"` | `"16:9"` | `"4:3"` | `"9:16"`
|
|
64
|
+
- `aspect` (required): `"1:1"` | `"16:9"` | `"4:3"` | `"9:16"` | `"4:1"`
|
|
65
65
|
- `alt` (string, optional)
|
|
66
|
+
- `title` (string, optional, max 80) and `subtitle` (string, optional, max 120) render readable overlay text. Use this for hero-like image titles; do not create a separate hero component.
|
|
66
67
|
|
|
67
68
|
**item** — Content row matching shadcn Item: optional left media, content, and right-side actions slot.
|
|
68
69
|
- `title` (string, required, max 100)
|
|
@@ -84,6 +85,7 @@ Top-level fields: `version` (required, `"1.0"` or `"2.0"`), `theme` (optional, `
|
|
|
84
85
|
- `size` (optional): `"md"` (body) | `"sm"` (caption). Default: `"md"`
|
|
85
86
|
- `weight` (optional): `"bold"` | `"normal"`. Default: `"normal"`
|
|
86
87
|
- `align` (optional): `"left"` | `"center"` | `"right"`. Default: `"left"`
|
|
88
|
+
- `maxLines` (optional): integer 1–6. Default: no clamp. Set this when body text should use a bounded number of visible lines.
|
|
87
89
|
|
|
88
90
|
### Data Components
|
|
89
91
|
|
|
@@ -100,14 +102,27 @@ Top-level fields: `version` (required, `"1.0"` or `"2.0"`), `theme` (optional, `
|
|
|
100
102
|
- `gap` (optional): `"none"` (0px) | `"sm"` (1px) | `"md"` (2px) | `"lg"` (4px). Default: `"sm"`
|
|
101
103
|
- `cellAspectRatio` (optional): `"auto"` | `"square"`. Default: `"auto"`. Use `"square"` for game boards whose cells must stay square as snap width changes.
|
|
102
104
|
- `rowHeight` (number, optional, 8–64): pixel height per row. Default: 28. Grid height = rows × rowHeight
|
|
105
|
+
- `maxWidth` (optional): `"sm"` | `"md"` | `"lg"`. Default: `"lg"` (full-width). Use `sm`/`md` with `cellAspectRatio: "square"` for small centered boards that should not stretch full-width.
|
|
103
106
|
- `select` (optional): `"off"` | `"single"` | `"multiple"`. Default: `"off"`. With `select: "off"`, bind `on.press` for press-to-act (each press writes the cell's `value` or `"row,col"` to `inputs[name]` and fires the action). With `"single"` / `"multiple"`, presses accumulate selection state and pair with a separate submit `button` (multi-select joins values with `|`); `on.press` is ignored.
|
|
104
107
|
- Events: `press` — fires on cell press, only when `select: "off"`; `inputs[name]` is set to the pressed cell's `value` (or `"row,col"` fallback) before the bound action runs
|
|
105
108
|
|
|
106
109
|
### Container Components
|
|
107
110
|
|
|
111
|
+
**paginator** — Client-side page navigation without a server round trip.
|
|
112
|
+
- Children are page element IDs; one child renders at a time.
|
|
113
|
+
- `initialPage` (optional): zero-based page index. Default: `0`
|
|
114
|
+
- `showIndicators` (optional): boolean. Default: `true`
|
|
115
|
+
- `showControls` (optional): boolean. Default: `true`
|
|
116
|
+
- `controlsPosition` (optional): `"top"` | `"bottom"`. Default: `"bottom"`. Moves the built-in pagination bar above or below page content; use `"top"` to keep controls stable when page heights vary.
|
|
117
|
+
- `transition` (optional): `"slide"` | `"fade"` | `"scale"` | `"none"`. Default: `"slide"`. Controls the local page-change animation.
|
|
118
|
+
- The current page is json-render local UI state only and is never included in POST `inputs`.
|
|
119
|
+
- Set both `showControls: false` and `showIndicators: false` to hide the built-in pagination bar.
|
|
120
|
+
- Buttons or tappable `cell_grid` cells anywhere in the same snap can bind local actions: `paginator_next` with `params: {}`, `paginator_prev` with `params: {}`, and `paginator_go_to` with `params: { "page": 0 }`. These affect the snap's rendered paginator, never POST, and support one paginator per snap in this release.
|
|
121
|
+
- Paginator children may exceed the normal per-container child limit, but the snap still must stay within 64 total elements and max nesting depth.
|
|
122
|
+
|
|
108
123
|
**stack** — Layout container.
|
|
109
124
|
- `direction` (optional): `"vertical"` | `"horizontal"`. Default: `"vertical"`
|
|
110
|
-
- `gap` (optional): `"none"` | `"sm"` | `"md"` | `"lg"`. Vertical px: 0/
|
|
125
|
+
- `gap` (optional): `"none"` | `"sm"` | `"md"` | `"lg"`. Vertical px: 0/4/16/24. Horizontal px: 0/4/8/16 (tighter, since children sit side-by-side). Default for vertical: `"md"`. Default for horizontal is column-aware: 2 cols → `"lg"` (16px), 3 cols → `"md"` (8px), 4+ cols → `"sm"` (4px), unknown → `"md"` (8px). An explicit value always wins — override the default when you have a deliberate visual reason (e.g. tighter toolbar, extra breathing room around a hero row).
|
|
111
126
|
- `justify` (optional): `"start"` | `"center"` | `"end"` | `"between"` | `"around"`
|
|
112
127
|
- `columns` (optional, horizontal only): `2`–`6` — CSS grid with equal columns (mixed children or layout that needs fixed column counts).
|
|
113
128
|
- Children are element IDs
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
|
@@ -21,6 +21,7 @@ export const GRID_MIN_ROWS = 2;
|
|
|
21
21
|
export const GRID_MAX_ROWS = 16;
|
|
22
22
|
export const GRID_GAP_VALUES = ["none", "sm", "md", "lg"] as const;
|
|
23
23
|
export const GRID_CELL_ASPECT_RATIO_VALUES = ["auto", "square"] as const;
|
|
24
|
+
export const GRID_MAX_WIDTH_VALUES = ["sm", "md", "lg"] as const;
|
|
24
25
|
|
|
25
26
|
// ─── Snap structural limits ───────────────────────────
|
|
26
27
|
export const MAX_ELEMENTS = 64;
|
|
@@ -9,6 +9,7 @@ import { SnapImage } from "./components/image";
|
|
|
9
9
|
import { SnapInput } from "./components/input";
|
|
10
10
|
import { SnapItem } from "./components/item";
|
|
11
11
|
import { SnapItemGroup } from "./components/item-group";
|
|
12
|
+
import { SnapPaginator } from "./components/paginator";
|
|
12
13
|
import { SnapProgress } from "./components/progress";
|
|
13
14
|
import { SnapSeparator } from "./components/separator";
|
|
14
15
|
import { SnapSlider } from "./components/slider";
|
|
@@ -31,6 +32,7 @@ export const SnapCatalogView = createRenderer(snapJsonRenderCatalog, {
|
|
|
31
32
|
input: SnapInput,
|
|
32
33
|
item: SnapItem,
|
|
33
34
|
item_group: SnapItemGroup,
|
|
35
|
+
paginator: SnapPaginator,
|
|
34
36
|
progress: SnapProgress,
|
|
35
37
|
separator: SnapSeparator,
|
|
36
38
|
slider: SnapSlider,
|