convex-feedback-ui 0.2.0-beta.2 → 0.2.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +40 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/native/expo/FeedbackRoutes.d.ts.map +1 -1
  4. package/dist/native/expo/FeedbackRoutes.js +15 -2
  5. package/dist/native/expo/FeedbackRoutes.js.map +1 -1
  6. package/dist/native/expo/RoadmapRoutes.d.ts +3 -0
  7. package/dist/native/expo/RoadmapRoutes.d.ts.map +1 -0
  8. package/dist/native/expo/RoadmapRoutes.js +61 -0
  9. package/dist/native/expo/RoadmapRoutes.js.map +1 -0
  10. package/dist/native/expo/RoadmapScreen.d.ts +24 -0
  11. package/dist/native/expo/RoadmapScreen.d.ts.map +1 -0
  12. package/dist/native/expo/RoadmapScreen.js +43 -0
  13. package/dist/native/expo/RoadmapScreen.js.map +1 -0
  14. package/dist/native/expo/RoadmapStackLayout.d.ts +8 -0
  15. package/dist/native/expo/RoadmapStackLayout.d.ts.map +1 -0
  16. package/dist/native/expo/RoadmapStackLayout.js +55 -0
  17. package/dist/native/expo/RoadmapStackLayout.js.map +1 -0
  18. package/dist/native/expo/RoutedRoadmapContext.d.ts +4 -0
  19. package/dist/native/expo/RoutedRoadmapContext.d.ts.map +1 -0
  20. package/dist/native/expo/RoutedRoadmapContext.js +11 -0
  21. package/dist/native/expo/RoutedRoadmapContext.js.map +1 -0
  22. package/dist/native/expo/index.d.ts +5 -1
  23. package/dist/native/expo/index.d.ts.map +1 -1
  24. package/dist/native/expo/index.js +5 -1
  25. package/dist/native/expo/index.js.map +1 -1
  26. package/dist/native/expo/routes.d.ts +17 -1
  27. package/dist/native/expo/routes.d.ts.map +1 -1
  28. package/dist/native/expo/routes.js +46 -0
  29. package/dist/native/expo/routes.js.map +1 -1
  30. package/dist/native/expo/types.d.ts +35 -1
  31. package/dist/native/expo/types.d.ts.map +1 -1
  32. package/dist/native/native/index.d.ts +1 -0
  33. package/dist/native/native/index.d.ts.map +1 -1
  34. package/dist/native/native/index.js +1 -0
  35. package/dist/native/native/index.js.map +1 -1
  36. package/dist/native/shared/ui/EntryCard.d.ts +1 -1
  37. package/dist/native/shared/ui/EntryCard.d.ts.map +1 -1
  38. package/dist/native/shared/ui/EntryCard.js +2 -2
  39. package/dist/native/shared/ui/EntryCard.js.map +1 -1
  40. package/dist/native/shared/ui/RoadmapBoard.d.ts +55 -0
  41. package/dist/native/shared/ui/RoadmapBoard.d.ts.map +1 -0
  42. package/dist/native/shared/ui/RoadmapBoard.js +89 -0
  43. package/dist/native/shared/ui/RoadmapBoard.js.map +1 -0
  44. package/dist/native/shared/ui/RoadmapScreen.d.ts +36 -1
  45. package/dist/native/shared/ui/RoadmapScreen.d.ts.map +1 -1
  46. package/dist/native/shared/ui/RoadmapScreen.js +143 -51
  47. package/dist/native/shared/ui/RoadmapScreen.js.map +1 -1
  48. package/dist/native/shared/ui/primitives.d.ts +1 -1
  49. package/dist/native/shared/ui/primitives.d.ts.map +1 -1
  50. package/dist/native/shared/ui/primitives.js +3 -1
  51. package/dist/native/shared/ui/primitives.js.map +1 -1
  52. package/dist/react/roadmap.d.ts.map +1 -1
  53. package/dist/react/roadmap.js +25 -13
  54. package/dist/react/roadmap.js.map +1 -1
  55. package/dist/react/styles.css +129 -0
  56. package/dist/shared/messages.d.ts.map +1 -1
  57. package/dist/shared/messages.js +24 -0
  58. package/dist/shared/messages.js.map +1 -1
  59. package/dist/shared/types/messages.d.ts +28 -1
  60. package/dist/shared/types/messages.d.ts.map +1 -1
  61. package/dist/shared/types/roadmap.d.ts +10 -1
  62. package/dist/shared/types/roadmap.d.ts.map +1 -1
  63. package/dist/shared/types/screen.d.ts +2 -0
  64. package/dist/shared/types/screen.d.ts.map +1 -1
  65. package/package.json +2 -2
  66. package/src/native/expo/FeedbackRoutes.tsx +30 -7
  67. package/src/native/expo/README.md +10 -2
  68. package/src/native/expo/RoadmapRoutes.tsx +171 -0
  69. package/src/native/expo/RoadmapScreen.tsx +177 -0
  70. package/src/native/expo/RoadmapStackLayout.tsx +110 -0
  71. package/src/native/expo/RoutedRoadmapContext.tsx +21 -0
  72. package/src/native/expo/index.ts +10 -1
  73. package/src/native/expo/routes.ts +69 -1
  74. package/src/native/expo/types.ts +43 -0
  75. package/src/native/native/README.md +9 -3
  76. package/src/native/native/index.ts +1 -0
  77. package/src/native/shared/ui/EntryCard.tsx +2 -1
  78. package/src/native/shared/ui/RoadmapBoard.tsx +225 -0
  79. package/src/native/shared/ui/RoadmapScreen.tsx +415 -242
  80. package/src/native/shared/ui/primitives.tsx +3 -0
  81. package/src/react/roadmap.tsx +140 -85
  82. package/src/react/styles.css +129 -0
  83. package/src/shared/messages.ts +25 -0
  84. package/src/shared/types/messages.ts +41 -1
  85. package/src/shared/types/roadmap.ts +18 -1
  86. package/src/shared/types/screen.ts +2 -0
@@ -10,6 +10,7 @@ export function EntryCard({
10
10
  entry,
11
11
  hooks,
12
12
  onOpen,
13
+ titleNumberOfLines,
13
14
  }: FeedbackScreenEntryCardProps) {
14
15
  const { isAuthenticated, onUnauthenticated } = useFeedbackBody();
15
16
  const setUpvote = hooks.useSetEntryUpvote();
@@ -46,7 +47,7 @@ export function EntryCard({
46
47
  <FeedbackEntry.Kind />
47
48
  <FeedbackEntry.Status />
48
49
  </View>
49
- <FeedbackEntry.Title />
50
+ <FeedbackEntry.Title numberOfLines={titleNumberOfLines} />
50
51
  <FeedbackEntry.Body numberOfLines={3} />
51
52
  <FeedbackEntry.CommentCount />
52
53
  </Pressable>
@@ -0,0 +1,225 @@
1
+ import type { RoadmapItem, RoadmapStatus } from "convex-feedback";
2
+ import type { ReactElement, ReactNode } from "react";
3
+ import {
4
+ ActivityIndicator,
5
+ FlatList,
6
+ Pressable,
7
+ ScrollView,
8
+ StyleSheet,
9
+ Text,
10
+ View,
11
+ type StyleProp,
12
+ type ViewProps,
13
+ type ViewStyle,
14
+ } from "react-native";
15
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
16
+
17
+ export interface RoadmapBoardStage {
18
+ value: RoadmapStatus;
19
+ label: string;
20
+ }
21
+
22
+ export interface RoadmapBoardColors {
23
+ background: string;
24
+ surface: string;
25
+ text: string;
26
+ muted: string;
27
+ border: string;
28
+ primary: string;
29
+ }
30
+
31
+ export interface RoadmapBoardItemRenderArgs {
32
+ item: RoadmapItem;
33
+ stage: RoadmapBoardStage;
34
+ stageIndex: number;
35
+ onOpen: () => void;
36
+ }
37
+
38
+ export interface RoadmapBoardProps {
39
+ /** Items are grouped into columns using their current status. */
40
+ items: readonly RoadmapItem[];
41
+ /** Ordered columns rendered from left to right. */
42
+ stages: readonly RoadmapBoardStage[];
43
+ /** Colors used by the shared board frame and its loading/empty states. */
44
+ colors: RoadmapBoardColors;
45
+ /** Shows the same full-board loading state as the admin roadmap. */
46
+ loading?: boolean;
47
+ /** Copy shown when a column does not contain any items. */
48
+ emptyLabel: string;
49
+ /** Opens a roadmap item from any rendered card. */
50
+ onItemOpen: (item: RoadmapItem) => void;
51
+ /** Supplies the card UI so admin and user-facing cards can differ. */
52
+ renderItem: (args: RoadmapBoardItemRenderArgs) => ReactElement | null;
53
+ style?: StyleProp<ViewStyle>;
54
+ contentContainerStyle?: StyleProp<ViewStyle>;
55
+ }
56
+
57
+ export interface RoadmapBoardCardProps extends Pick<
58
+ ViewProps,
59
+ "accessibilityLabel"
60
+ > {
61
+ colors: Pick<RoadmapBoardColors, "border" | "surface">;
62
+ onPress: () => void;
63
+ children: ReactNode;
64
+ style?: StyleProp<ViewStyle>;
65
+ }
66
+
67
+ /** Shared admin-style pressable card shell with configurable contents. */
68
+ export function RoadmapBoardCard({
69
+ colors,
70
+ onPress,
71
+ accessibilityLabel,
72
+ children,
73
+ style,
74
+ }: RoadmapBoardCardProps) {
75
+ return (
76
+ <Pressable
77
+ accessibilityRole="button"
78
+ accessibilityLabel={accessibilityLabel}
79
+ onPress={onPress}
80
+ style={({ pressed }) => [
81
+ styles.card,
82
+ {
83
+ borderColor: colors.border,
84
+ backgroundColor: colors.surface,
85
+ },
86
+ style,
87
+ pressed && styles.pressed,
88
+ ]}
89
+ >
90
+ {children}
91
+ </Pressable>
92
+ );
93
+ }
94
+
95
+ /**
96
+ * Shared stage-board layout used by the public roadmap and admin app.
97
+ *
98
+ * The outer list scrolls horizontally while each stage owns its vertical
99
+ * `FlatList`, matching the admin board's bounded nested-scroll structure.
100
+ */
101
+ export function RoadmapBoard({
102
+ items,
103
+ stages,
104
+ colors,
105
+ loading = false,
106
+ emptyLabel,
107
+ onItemOpen,
108
+ renderItem,
109
+ style,
110
+ contentContainerStyle,
111
+ }: RoadmapBoardProps) {
112
+ const insets = useSafeAreaInsets();
113
+
114
+ if (loading) {
115
+ return (
116
+ <ActivityIndicator
117
+ style={[styles.loader, { backgroundColor: colors.background }, style]}
118
+ color={colors.primary}
119
+ />
120
+ );
121
+ }
122
+
123
+ return (
124
+ <ScrollView
125
+ horizontal
126
+ style={[
127
+ styles.boardScroll,
128
+ { backgroundColor: colors.background },
129
+ style,
130
+ ]}
131
+ contentInsetAdjustmentBehavior="automatic"
132
+ showsHorizontalScrollIndicator={false}
133
+ contentContainerStyle={[
134
+ styles.board,
135
+ {
136
+ paddingLeft: 12 + insets.left,
137
+ paddingRight: 12 + insets.right,
138
+ paddingBottom: 24 + insets.bottom,
139
+ },
140
+ contentContainerStyle,
141
+ ]}
142
+ >
143
+ {stages.map((stage, stageIndex) => {
144
+ const stageItems = items
145
+ .filter((item) => item.status === stage.value)
146
+ .sort((a, b) => a.position - b.position);
147
+
148
+ return (
149
+ <View
150
+ key={stage.value}
151
+ style={[
152
+ styles.column,
153
+ {
154
+ borderColor: colors.border,
155
+ backgroundColor: colors.surface,
156
+ },
157
+ ]}
158
+ >
159
+ <View style={[styles.columnHeader, { borderColor: colors.border }]}>
160
+ <Text style={[styles.columnTitle, { color: colors.text }]}>
161
+ {stage.label}
162
+ </Text>
163
+ <Text style={[styles.count, { color: colors.muted }]}>
164
+ {stageItems.length}
165
+ </Text>
166
+ </View>
167
+ <FlatList
168
+ data={stageItems}
169
+ nestedScrollEnabled
170
+ contentInsetAdjustmentBehavior="never"
171
+ keyExtractor={(item) => item.id}
172
+ contentContainerStyle={[
173
+ styles.cards,
174
+ { paddingBottom: 16 + insets.bottom },
175
+ ]}
176
+ ListEmptyComponent={
177
+ <Text style={[styles.empty, { color: colors.muted }]}>
178
+ {emptyLabel}
179
+ </Text>
180
+ }
181
+ renderItem={({ item }) =>
182
+ renderItem({
183
+ item,
184
+ stage,
185
+ stageIndex,
186
+ onOpen: () => onItemOpen(item),
187
+ })
188
+ }
189
+ />
190
+ </View>
191
+ );
192
+ })}
193
+ </ScrollView>
194
+ );
195
+ }
196
+
197
+ const styles = StyleSheet.create({
198
+ boardScroll: { flex: 1 },
199
+ loader: { flex: 1 },
200
+ board: { gap: 12, paddingTop: 12 },
201
+ column: {
202
+ width: 310,
203
+ borderWidth: 1,
204
+ borderRadius: 16,
205
+ overflow: "hidden",
206
+ },
207
+ columnHeader: {
208
+ flexDirection: "row",
209
+ alignItems: "center",
210
+ justifyContent: "space-between",
211
+ borderBottomWidth: 1,
212
+ padding: 14,
213
+ },
214
+ columnTitle: { fontSize: 14, fontWeight: "700" },
215
+ count: { fontSize: 12 },
216
+ cards: { gap: 10, padding: 10 },
217
+ empty: { padding: 18, textAlign: "center" },
218
+ card: {
219
+ gap: 8,
220
+ borderWidth: 1,
221
+ borderRadius: 12,
222
+ padding: 13,
223
+ },
224
+ pressed: { opacity: 0.7 },
225
+ });