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.
- package/README.md +40 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/native/expo/FeedbackRoutes.d.ts.map +1 -1
- package/dist/native/expo/FeedbackRoutes.js +15 -2
- package/dist/native/expo/FeedbackRoutes.js.map +1 -1
- package/dist/native/expo/RoadmapRoutes.d.ts +3 -0
- package/dist/native/expo/RoadmapRoutes.d.ts.map +1 -0
- package/dist/native/expo/RoadmapRoutes.js +61 -0
- package/dist/native/expo/RoadmapRoutes.js.map +1 -0
- package/dist/native/expo/RoadmapScreen.d.ts +24 -0
- package/dist/native/expo/RoadmapScreen.d.ts.map +1 -0
- package/dist/native/expo/RoadmapScreen.js +43 -0
- package/dist/native/expo/RoadmapScreen.js.map +1 -0
- package/dist/native/expo/RoadmapStackLayout.d.ts +8 -0
- package/dist/native/expo/RoadmapStackLayout.d.ts.map +1 -0
- package/dist/native/expo/RoadmapStackLayout.js +55 -0
- package/dist/native/expo/RoadmapStackLayout.js.map +1 -0
- package/dist/native/expo/RoutedRoadmapContext.d.ts +4 -0
- package/dist/native/expo/RoutedRoadmapContext.d.ts.map +1 -0
- package/dist/native/expo/RoutedRoadmapContext.js +11 -0
- package/dist/native/expo/RoutedRoadmapContext.js.map +1 -0
- package/dist/native/expo/index.d.ts +5 -1
- package/dist/native/expo/index.d.ts.map +1 -1
- package/dist/native/expo/index.js +5 -1
- package/dist/native/expo/index.js.map +1 -1
- package/dist/native/expo/routes.d.ts +17 -1
- package/dist/native/expo/routes.d.ts.map +1 -1
- package/dist/native/expo/routes.js +46 -0
- package/dist/native/expo/routes.js.map +1 -1
- package/dist/native/expo/types.d.ts +35 -1
- package/dist/native/expo/types.d.ts.map +1 -1
- package/dist/native/native/index.d.ts +1 -0
- package/dist/native/native/index.d.ts.map +1 -1
- package/dist/native/native/index.js +1 -0
- package/dist/native/native/index.js.map +1 -1
- package/dist/native/shared/ui/EntryCard.d.ts +1 -1
- package/dist/native/shared/ui/EntryCard.d.ts.map +1 -1
- package/dist/native/shared/ui/EntryCard.js +2 -2
- package/dist/native/shared/ui/EntryCard.js.map +1 -1
- package/dist/native/shared/ui/RoadmapBoard.d.ts +55 -0
- package/dist/native/shared/ui/RoadmapBoard.d.ts.map +1 -0
- package/dist/native/shared/ui/RoadmapBoard.js +89 -0
- package/dist/native/shared/ui/RoadmapBoard.js.map +1 -0
- package/dist/native/shared/ui/RoadmapScreen.d.ts +36 -1
- package/dist/native/shared/ui/RoadmapScreen.d.ts.map +1 -1
- package/dist/native/shared/ui/RoadmapScreen.js +143 -51
- package/dist/native/shared/ui/RoadmapScreen.js.map +1 -1
- package/dist/native/shared/ui/primitives.d.ts +1 -1
- package/dist/native/shared/ui/primitives.d.ts.map +1 -1
- package/dist/native/shared/ui/primitives.js +3 -1
- package/dist/native/shared/ui/primitives.js.map +1 -1
- package/dist/react/roadmap.d.ts.map +1 -1
- package/dist/react/roadmap.js +25 -13
- package/dist/react/roadmap.js.map +1 -1
- package/dist/react/styles.css +129 -0
- package/dist/shared/messages.d.ts.map +1 -1
- package/dist/shared/messages.js +24 -0
- package/dist/shared/messages.js.map +1 -1
- package/dist/shared/types/messages.d.ts +28 -1
- package/dist/shared/types/messages.d.ts.map +1 -1
- package/dist/shared/types/roadmap.d.ts +10 -1
- package/dist/shared/types/roadmap.d.ts.map +1 -1
- package/dist/shared/types/screen.d.ts +2 -0
- package/dist/shared/types/screen.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/native/expo/FeedbackRoutes.tsx +30 -7
- package/src/native/expo/README.md +10 -2
- package/src/native/expo/RoadmapRoutes.tsx +171 -0
- package/src/native/expo/RoadmapScreen.tsx +177 -0
- package/src/native/expo/RoadmapStackLayout.tsx +110 -0
- package/src/native/expo/RoutedRoadmapContext.tsx +21 -0
- package/src/native/expo/index.ts +10 -1
- package/src/native/expo/routes.ts +69 -1
- package/src/native/expo/types.ts +43 -0
- package/src/native/native/README.md +9 -3
- package/src/native/native/index.ts +1 -0
- package/src/native/shared/ui/EntryCard.tsx +2 -1
- package/src/native/shared/ui/RoadmapBoard.tsx +225 -0
- package/src/native/shared/ui/RoadmapScreen.tsx +415 -242
- package/src/native/shared/ui/primitives.tsx +3 -0
- package/src/react/roadmap.tsx +140 -85
- package/src/react/styles.css +129 -0
- package/src/shared/messages.ts +25 -0
- package/src/shared/types/messages.ts +41 -1
- package/src/shared/types/roadmap.ts +18 -1
- 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
|
+
});
|