paseo-form-render 0.0.0-stage → 0.1.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.
@@ -0,0 +1,687 @@
1
+ import type { PluginHostProps } from "@getpaseo/plugin/client";
2
+ import React, { useEffect, useMemo, useState } from "react";
3
+ import {
4
+ Modal,
5
+ Pressable,
6
+ ScrollView,
7
+ StyleSheet,
8
+ Text,
9
+ TextInput,
10
+ View,
11
+ } from "react-native";
12
+ import {
13
+ listProjectFilesRpc,
14
+ createDirectoryRpc,
15
+ type ProjectFileEntry,
16
+ type FilePickerShortcut,
17
+ } from "../shared/pickers";
18
+ import { normalizeExtensions } from "../shared/schema";
19
+ import { usePfrRpc } from "./rpc-bridge";
20
+
21
+ interface ProjectFileModalProps {
22
+ visible: boolean;
23
+ onClose: () => void;
24
+ onSelect: (selectedPath: string) => void;
25
+ agentId?: string;
26
+ pickerType?: "file" | "folder";
27
+ extensions?: string[] | string;
28
+ extension?: string;
29
+ initialPath?: string;
30
+ theme: PluginHostProps["theme"];
31
+ }
32
+
33
+ export function ProjectFileModal({
34
+ visible,
35
+ onClose,
36
+ onSelect,
37
+ agentId,
38
+ pickerType = "file",
39
+ extensions,
40
+ extension,
41
+ initialPath,
42
+ theme,
43
+ }: ProjectFileModalProps) {
44
+ const listProjectFiles = usePfrRpc(listProjectFilesRpc);
45
+ const createDir = usePfrRpc(createDirectoryRpc);
46
+ const [currentPath, setCurrentPath] = useState("");
47
+ const [parentPath, setParentPath] = useState<string | null>(null);
48
+ const [entries, setEntries] = useState<ProjectFileEntry[]>([]);
49
+ const [shortcuts, setShortcuts] = useState<FilePickerShortcut[]>([]);
50
+ const [isWorkspaceRelative, setIsWorkspaceRelative] = useState(true);
51
+ const [loading, setLoading] = useState(false);
52
+ const [error, setError] = useState<string | null>(null);
53
+ const [search, setSearch] = useState("");
54
+ const [selectedPath, setSelectedPath] = useState<string>("");
55
+ const [isCreatingFolder, setIsCreatingFolder] = useState(false);
56
+ const [newFolderName, setNewFolderName] = useState("");
57
+ const [creatingFolderLoading, setCreatingFolderLoading] = useState(false);
58
+ const [newFolderError, setNewFolderError] = useState<string | null>(null);
59
+
60
+ const cleanExts = useMemo(
61
+ () => (pickerType === "folder" ? undefined : normalizeExtensions(extensions, extension)),
62
+ [pickerType, extensions, extension],
63
+ );
64
+
65
+ useEffect(() => {
66
+ if (!visible) return;
67
+ const initPath = initialPath?.trim() || "";
68
+ const initDir =
69
+ initPath.includes("/") || initPath.includes("\\")
70
+ ? initPath.replace(/[/\\][^/\\]*$/, "")
71
+ : "";
72
+ setSelectedPath(initPath);
73
+ loadDirectory(initDir);
74
+ }, [visible, initialPath]);
75
+
76
+ async function loadDirectory(targetPath: string) {
77
+ if (!agentId) return;
78
+ setLoading(true);
79
+ setError(null);
80
+ setSearch("");
81
+ setIsCreatingFolder(false);
82
+ setNewFolderError(null);
83
+ try {
84
+ const res = await listProjectFiles({ agentId, targetPath });
85
+ if (res.parentPath !== undefined) {
86
+ setParentPath(res.parentPath);
87
+ }
88
+ if (res.currentPath !== undefined) {
89
+ setCurrentPath(res.currentPath);
90
+ }
91
+ if (res.shortcuts) {
92
+ setShortcuts(res.shortcuts);
93
+ }
94
+ if (res.isWorkspaceRelative !== undefined) {
95
+ setIsWorkspaceRelative(res.isWorkspaceRelative);
96
+ }
97
+
98
+ if (res.error) {
99
+ setError(res.error);
100
+ setEntries([]);
101
+ } else {
102
+ setEntries(res.entries);
103
+ }
104
+ } catch (err) {
105
+ setError(err instanceof Error ? err.message : String(err));
106
+ } finally {
107
+ setLoading(false);
108
+ }
109
+ }
110
+
111
+ const handleCreateFolderSubmit = async () => {
112
+ if (!agentId || !newFolderName.trim() || creatingFolderLoading) return;
113
+ const name = newFolderName.trim();
114
+ if (/[<>:"/\\|?*]/.test(name) || name === "." || name === "..") {
115
+ setNewFolderError('Folder name contains invalid characters (/ \\ : * ? " < > |).');
116
+ return;
117
+ }
118
+ setCreatingFolderLoading(true);
119
+ setNewFolderError(null);
120
+ try {
121
+ const res = await createDir({
122
+ agentId,
123
+ parentPath: currentPath || "",
124
+ name,
125
+ });
126
+ if (res.success && res.fullPath) {
127
+ setIsCreatingFolder(false);
128
+ setNewFolderName("");
129
+ setNewFolderError(null);
130
+ await loadDirectory(res.fullPath);
131
+ if (pickerType === "folder") {
132
+ setSelectedPath(res.relativePath || res.fullPath);
133
+ }
134
+ } else {
135
+ setNewFolderError(res.error || "Failed to create folder.");
136
+ }
137
+ } catch (err) {
138
+ setNewFolderError(err instanceof Error ? err.message : String(err));
139
+ } finally {
140
+ setCreatingFolderLoading(false);
141
+ }
142
+ };
143
+
144
+ const filteredEntries = useMemo(() => {
145
+ let result = entries;
146
+ if (search.trim()) {
147
+ const query = search.trim().toLowerCase();
148
+ result = result.filter((e) => e.name.toLowerCase().includes(query));
149
+ }
150
+ if (cleanExts && cleanExts.length > 0) {
151
+ result = result.filter((e) => {
152
+ if (e.isDirectory) return true;
153
+ const nameLower = e.name.toLowerCase();
154
+ return cleanExts.some((ext) => nameLower.endsWith("." + ext));
155
+ });
156
+ }
157
+ return result;
158
+ }, [entries, search, cleanExts]);
159
+
160
+ const breadcrumbs = useMemo(() => {
161
+ if (isWorkspaceRelative) {
162
+ if (!currentPath) return [{ label: "Project Root", path: "" }];
163
+ const parts = currentPath.split("/").filter(Boolean);
164
+ const crumbs = [{ label: "Project", path: "" }];
165
+ let accum = "";
166
+ for (const part of parts) {
167
+ accum = accum ? `${accum}/${part}` : part;
168
+ crumbs.push({ label: part, path: accum });
169
+ }
170
+ return crumbs;
171
+ } else {
172
+ const isWindows = /^[a-zA-Z]:/.test(currentPath);
173
+ const parts = currentPath.split(/[/\\]/).filter(Boolean);
174
+ const crumbs: { label: string; path: string }[] = [];
175
+ if (isWindows) {
176
+ const drive = parts[0];
177
+ crumbs.push({ label: drive, path: drive + "\\" });
178
+ let accum = drive + "\\";
179
+ for (let i = 1; i < parts.length; i++) {
180
+ accum = accum + (accum.endsWith("\\") ? "" : "\\") + parts[i];
181
+ crumbs.push({ label: parts[i], path: accum });
182
+ }
183
+ } else {
184
+ crumbs.push({ label: "/", path: "/" });
185
+ let accum = "";
186
+ for (const part of parts) {
187
+ accum = `${accum}/${part}`;
188
+ crumbs.push({ label: part, path: accum });
189
+ }
190
+ }
191
+ return crumbs;
192
+ }
193
+ }, [currentPath, isWorkspaceRelative]);
194
+
195
+ const handleEntryPress = (entry: ProjectFileEntry) => {
196
+ if (entry.isDirectory) {
197
+ loadDirectory(entry.fullPath || entry.relativePath);
198
+ if (pickerType === "folder") {
199
+ setSelectedPath(entry.relativePath);
200
+ }
201
+ } else {
202
+ if (pickerType !== "folder") {
203
+ setSelectedPath(entry.relativePath);
204
+ }
205
+ }
206
+ };
207
+
208
+ const effectiveSelected =
209
+ selectedPath || (pickerType === "folder" ? currentPath || "." : "");
210
+
211
+ const handleConfirm = () => {
212
+ if (effectiveSelected) {
213
+ onSelect(effectiveSelected);
214
+ onClose();
215
+ }
216
+ };
217
+
218
+ const colors = theme.colors;
219
+
220
+ if (!visible) return null;
221
+
222
+ return (
223
+ <Modal visible={visible} transparent animationType="fade" onRequestClose={onClose}>
224
+ <View style={styles.backdrop}>
225
+ <View style={[styles.dialog, { backgroundColor: colors.surface0, borderColor: colors.border }]}>
226
+ {/* Header */}
227
+ <View style={[styles.header, { borderBottomColor: colors.border }]}>
228
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 8, flex: 1, marginRight: 8 }}>
229
+ <Text style={[styles.title, { color: colors.foreground }]}>
230
+ {pickerType === "folder" ? "Select Folder" : "Select File"}
231
+ </Text>
232
+ {cleanExts && cleanExts.length > 0 && (
233
+ <View style={[styles.filterBadge, { backgroundColor: colors.surface1, borderColor: colors.border }]}>
234
+ <Text style={[styles.filterBadgeText, { color: colors.accent }]}>
235
+ {cleanExts.map((e) => `.${e}`).join(", ")}
236
+ </Text>
237
+ </View>
238
+ )}
239
+ </View>
240
+ <Pressable onPress={onClose} style={styles.closeBtn}>
241
+ <Text style={{ color: colors.foregroundMuted, fontSize: 18, fontWeight: "600" }}>✕</Text>
242
+ </Pressable>
243
+ </View>
244
+
245
+ {/* Quick Shortcuts Bar */}
246
+ {shortcuts.length > 0 && (
247
+ <View style={[styles.shortcutsBar, { borderBottomColor: colors.border, backgroundColor: colors.surface1 }]}>
248
+ <Text style={[styles.shortcutsLabel, { color: colors.foregroundMuted }]}>Quick:</Text>
249
+ <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.shortcutsList}>
250
+ {shortcuts.map((sc) => (
251
+ <Pressable
252
+ key={sc.id}
253
+ onPress={() => loadDirectory(sc.path)}
254
+ style={[
255
+ styles.shortcutPill,
256
+ {
257
+ backgroundColor: colors.surface2,
258
+ borderColor: colors.border,
259
+ },
260
+ ]}
261
+ >
262
+ <Text style={styles.shortcutIcon}>{sc.icon}</Text>
263
+ <Text style={[styles.shortcutText, { color: colors.foreground }]}>{sc.label}</Text>
264
+ </Pressable>
265
+ ))}
266
+ </ScrollView>
267
+ </View>
268
+ )}
269
+
270
+ {/* Breadcrumbs */}
271
+ <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.breadcrumbBar}>
272
+ {breadcrumbs.map((crumb, idx) => (
273
+ <React.Fragment key={crumb.path}>
274
+ {idx > 0 && <Text style={{ color: colors.foregroundMuted, marginHorizontal: 4 }}>/</Text>}
275
+ <Pressable onPress={() => loadDirectory(crumb.path)}>
276
+ <Text
277
+ style={{
278
+ color: idx === breadcrumbs.length - 1 ? colors.foreground : colors.accent,
279
+ fontWeight: idx === breadcrumbs.length - 1 ? "600" : "normal",
280
+ fontSize: 13,
281
+ }}
282
+ >
283
+ {crumb.label}
284
+ </Text>
285
+ </Pressable>
286
+ </React.Fragment>
287
+ ))}
288
+ </ScrollView>
289
+
290
+ {/* Search Bar & Actions */}
291
+ <View style={styles.searchContainer}>
292
+ <TextInput
293
+ style={[
294
+ styles.searchInput,
295
+ {
296
+ backgroundColor: colors.surface1,
297
+ color: colors.foreground,
298
+ borderColor: colors.border,
299
+ },
300
+ ]}
301
+ placeholder="Search files/folders in directory..."
302
+ placeholderTextColor={colors.foregroundMuted}
303
+ value={search}
304
+ onChangeText={setSearch}
305
+ />
306
+ {pickerType === "folder" && (
307
+ <Pressable
308
+ onPress={() => {
309
+ setIsCreatingFolder((prev) => !prev);
310
+ setNewFolderName("");
311
+ setNewFolderError(null);
312
+ }}
313
+ style={[
314
+ styles.newFolderBtn,
315
+ {
316
+ backgroundColor: isCreatingFolder ? colors.surface2 : colors.surface1,
317
+ borderColor: isCreatingFolder ? colors.accent : colors.border,
318
+ },
319
+ ]}
320
+ >
321
+ <Text
322
+ style={{
323
+ color: isCreatingFolder ? colors.accent : colors.foreground,
324
+ fontSize: 12,
325
+ fontWeight: "600",
326
+ }}
327
+ >
328
+ 📁+ New Folder
329
+ </Text>
330
+ </Pressable>
331
+ )}
332
+ </View>
333
+
334
+ {/* Inline New Folder Creation Row */}
335
+ {isCreatingFolder && (
336
+ <View style={{ marginHorizontal: 16, marginBottom: 8 }}>
337
+ <View
338
+ style={[
339
+ styles.newFolderRow,
340
+ {
341
+ backgroundColor: colors.surface1,
342
+ borderColor: newFolderError ? colors.statusDanger : colors.accent,
343
+ },
344
+ ]}
345
+ >
346
+ <Text style={styles.entryIcon}>📁</Text>
347
+ <TextInput
348
+ style={[
349
+ styles.newFolderInput,
350
+ {
351
+ color: colors.foreground,
352
+ backgroundColor: colors.surface0,
353
+ borderColor: colors.border,
354
+ },
355
+ ]}
356
+ placeholder="New folder name..."
357
+ placeholderTextColor={colors.foregroundMuted}
358
+ value={newFolderName}
359
+ onChangeText={(t) => {
360
+ setNewFolderName(t);
361
+ if (newFolderError) setNewFolderError(null);
362
+ }}
363
+ autoFocus
364
+ onSubmitEditing={handleCreateFolderSubmit}
365
+ />
366
+ <Pressable
367
+ disabled={creatingFolderLoading || !newFolderName.trim()}
368
+ onPress={handleCreateFolderSubmit}
369
+ style={[
370
+ styles.newFolderActionBtn,
371
+ {
372
+ backgroundColor: colors.accent,
373
+ opacity: creatingFolderLoading || !newFolderName.trim() ? 0.6 : 1,
374
+ },
375
+ ]}
376
+ >
377
+ <Text style={{ color: colors.surface0, fontSize: 12, fontWeight: "600" }}>
378
+ {creatingFolderLoading ? "..." : "Create"}
379
+ </Text>
380
+ </Pressable>
381
+ <Pressable
382
+ onPress={() => {
383
+ setIsCreatingFolder(false);
384
+ setNewFolderName("");
385
+ setNewFolderError(null);
386
+ }}
387
+ style={[styles.newFolderActionBtn, { backgroundColor: colors.surface2 }]}
388
+ >
389
+ <Text style={{ color: colors.foregroundMuted, fontSize: 12, fontWeight: "600" }}>
390
+ Cancel
391
+ </Text>
392
+ </Pressable>
393
+ </View>
394
+ {newFolderError && (
395
+ <Text style={{ color: colors.statusDanger, fontSize: 12, marginTop: 4 }}>
396
+ {newFolderError}
397
+ </Text>
398
+ )}
399
+ </View>
400
+ )}
401
+
402
+ {/* Directory Content List */}
403
+ <ScrollView style={styles.listContainer}>
404
+ {parentPath !== null && (
405
+ <Pressable
406
+ style={[styles.entryRow, { borderBottomColor: colors.surface2 }]}
407
+ onPress={() => loadDirectory(parentPath)}
408
+ >
409
+ <Text style={styles.entryIcon}>📁</Text>
410
+ <Text style={{ color: colors.foregroundMuted, fontSize: 14 }}>.. (Up to parent)</Text>
411
+ </Pressable>
412
+ )}
413
+
414
+ {loading ? (
415
+ <Text style={[styles.emptyText, { color: colors.foregroundMuted }]}>Loading files...</Text>
416
+ ) : error ? (
417
+ <Text style={[styles.emptyText, { color: colors.statusDanger }]}>{error}</Text>
418
+ ) : filteredEntries.length === 0 ? (
419
+ <Text style={[styles.emptyText, { color: colors.foregroundMuted }]}>No matching entries found</Text>
420
+ ) : (
421
+ filteredEntries.map((entry) => {
422
+ const isSelected = selectedPath === entry.relativePath;
423
+ return (
424
+ <Pressable
425
+ key={entry.relativePath}
426
+ style={[
427
+ styles.entryRow,
428
+ {
429
+ backgroundColor: isSelected ? colors.surface2 : "transparent",
430
+ borderBottomColor: colors.surface1,
431
+ },
432
+ ]}
433
+ onPress={() => handleEntryPress(entry)}
434
+ >
435
+ <Text style={styles.entryIcon}>{entry.isDirectory ? "📁" : "📄"}</Text>
436
+ <Text
437
+ style={[
438
+ styles.entryName,
439
+ {
440
+ color: colors.foreground,
441
+ fontWeight: entry.isDirectory ? "600" : "normal",
442
+ },
443
+ ]}
444
+ >
445
+ {entry.name}
446
+ </Text>
447
+ {isSelected && (
448
+ <Text style={{ color: colors.accent, fontWeight: "600", fontSize: 13 }}>✓ Selected</Text>
449
+ )}
450
+ </Pressable>
451
+ );
452
+ })
453
+ )}
454
+ </ScrollView>
455
+
456
+ {/* Footer */}
457
+ <View style={[styles.footer, { borderTopColor: colors.border }]}>
458
+ <View style={{ flex: 1, marginRight: 10, overflow: "hidden" }}>
459
+ <Text style={{ color: colors.foregroundMuted, fontSize: 12 }}>Selected:</Text>
460
+ {effectiveSelected ? (
461
+ <ScrollView
462
+ horizontal
463
+ showsHorizontalScrollIndicator={false}
464
+ contentContainerStyle={{ alignItems: "center" }}
465
+ style={{ marginTop: 2 }}
466
+ >
467
+ <Text
468
+ style={{ color: colors.foreground, fontSize: 13, fontWeight: "500" }}
469
+ >
470
+ {effectiveSelected}
471
+ </Text>
472
+ </ScrollView>
473
+ ) : (
474
+ <Text
475
+ style={{
476
+ color: colors.foregroundMuted,
477
+ fontSize: 13,
478
+ fontStyle: "italic",
479
+ marginTop: 2,
480
+ }}
481
+ >
482
+ (none selected)
483
+ </Text>
484
+ )}
485
+ </View>
486
+
487
+ <View style={{ flexDirection: "row", gap: 8 }}>
488
+ <Pressable
489
+ onPress={onClose}
490
+ style={[styles.actionBtn, { borderColor: colors.border, borderWidth: 1 }]}
491
+ >
492
+ <Text style={{ color: colors.foreground, fontSize: 13 }}>Cancel</Text>
493
+ </Pressable>
494
+
495
+ <Pressable
496
+ onPress={handleConfirm}
497
+ disabled={!effectiveSelected}
498
+ style={[
499
+ styles.actionBtn,
500
+ {
501
+ backgroundColor: effectiveSelected ? colors.accent : colors.surface2,
502
+ opacity: effectiveSelected ? 1 : 0.6,
503
+ },
504
+ ]}
505
+ >
506
+ <Text style={{ color: "#fff", fontWeight: "600", fontSize: 13 }}>
507
+ {pickerType === "folder" ? "Choose Folder" : "Choose File"}
508
+ </Text>
509
+ </Pressable>
510
+ </View>
511
+ </View>
512
+ </View>
513
+ </View>
514
+ </Modal>
515
+ );
516
+ }
517
+
518
+ const styles = StyleSheet.create({
519
+ backdrop: {
520
+ flex: 1,
521
+ backgroundColor: "rgba(0, 0, 0, 0.55)",
522
+ justifyContent: "center",
523
+ alignItems: "center",
524
+ padding: 20,
525
+ },
526
+ dialog: {
527
+ width: "100%",
528
+ maxWidth: 720,
529
+ maxHeight: "85%",
530
+ borderRadius: 12,
531
+ borderWidth: 1,
532
+ overflow: "hidden",
533
+ display: "flex",
534
+ flexDirection: "column",
535
+ },
536
+ header: {
537
+ flexDirection: "row",
538
+ justifyContent: "space-between",
539
+ alignItems: "center",
540
+ paddingHorizontal: 16,
541
+ paddingVertical: 14,
542
+ borderBottomWidth: 1,
543
+ },
544
+ title: {
545
+ fontSize: 16,
546
+ fontWeight: "600",
547
+ },
548
+ filterBadge: {
549
+ paddingHorizontal: 7,
550
+ paddingVertical: 2,
551
+ borderRadius: 4,
552
+ borderWidth: 1,
553
+ },
554
+ filterBadgeText: {
555
+ fontSize: 11,
556
+ fontWeight: "600",
557
+ },
558
+ closeBtn: {
559
+ padding: 4,
560
+ },
561
+ shortcutsBar: {
562
+ flexDirection: "row",
563
+ alignItems: "center",
564
+ paddingHorizontal: 16,
565
+ paddingVertical: 8,
566
+ borderBottomWidth: 1,
567
+ gap: 8,
568
+ },
569
+ shortcutsLabel: {
570
+ fontSize: 12,
571
+ fontWeight: "600",
572
+ },
573
+ shortcutsList: {
574
+ flexDirection: "row",
575
+ alignItems: "center",
576
+ gap: 6,
577
+ },
578
+ shortcutPill: {
579
+ flexDirection: "row",
580
+ alignItems: "center",
581
+ paddingHorizontal: 8,
582
+ paddingVertical: 4,
583
+ borderRadius: 6,
584
+ borderWidth: 1,
585
+ gap: 4,
586
+ },
587
+ shortcutIcon: {
588
+ fontSize: 13,
589
+ },
590
+ shortcutText: {
591
+ fontSize: 12,
592
+ fontWeight: "500",
593
+ },
594
+ breadcrumbBar: {
595
+ flexDirection: "row",
596
+ paddingHorizontal: 16,
597
+ paddingVertical: 10,
598
+ maxHeight: 40,
599
+ },
600
+ searchContainer: {
601
+ flexDirection: "row",
602
+ alignItems: "center",
603
+ paddingHorizontal: 16,
604
+ paddingBottom: 10,
605
+ gap: 8,
606
+ },
607
+ searchInput: {
608
+ flex: 1,
609
+ borderWidth: 1,
610
+ borderRadius: 6,
611
+ paddingHorizontal: 10,
612
+ paddingVertical: 6,
613
+ fontSize: 13,
614
+ },
615
+ newFolderBtn: {
616
+ flexDirection: "row",
617
+ alignItems: "center",
618
+ gap: 4,
619
+ borderWidth: 1,
620
+ borderRadius: 6,
621
+ paddingHorizontal: 10,
622
+ paddingVertical: 6,
623
+ },
624
+ newFolderRow: {
625
+ flexDirection: "row",
626
+ alignItems: "center",
627
+ paddingHorizontal: 10,
628
+ paddingVertical: 7,
629
+ borderWidth: 1,
630
+ borderRadius: 6,
631
+ gap: 8,
632
+ },
633
+ newFolderInput: {
634
+ flex: 1,
635
+ borderWidth: 1,
636
+ borderRadius: 4,
637
+ paddingHorizontal: 8,
638
+ paddingVertical: 4,
639
+ fontSize: 13,
640
+ },
641
+ newFolderActionBtn: {
642
+ paddingHorizontal: 10,
643
+ paddingVertical: 6,
644
+ borderRadius: 4,
645
+ },
646
+ listContainer: {
647
+ flex: 1,
648
+ minHeight: 220,
649
+ paddingHorizontal: 16,
650
+ },
651
+ entryRow: {
652
+ flexDirection: "row",
653
+ alignItems: "center",
654
+ paddingVertical: 9,
655
+ paddingHorizontal: 8,
656
+ borderRadius: 6,
657
+ borderBottomWidth: StyleSheet.hairlineWidth,
658
+ },
659
+ entryIcon: {
660
+ fontSize: 16,
661
+ marginRight: 10,
662
+ },
663
+ entryName: {
664
+ fontSize: 14,
665
+ flex: 1,
666
+ },
667
+ emptyText: {
668
+ textAlign: "center",
669
+ paddingVertical: 32,
670
+ fontSize: 13,
671
+ },
672
+ footer: {
673
+ flexDirection: "row",
674
+ alignItems: "center",
675
+ justifyContent: "space-between",
676
+ paddingHorizontal: 16,
677
+ paddingVertical: 12,
678
+ borderTopWidth: 1,
679
+ },
680
+ actionBtn: {
681
+ paddingHorizontal: 14,
682
+ paddingVertical: 8,
683
+ borderRadius: 6,
684
+ alignItems: "center",
685
+ justifyContent: "center",
686
+ },
687
+ });
@@ -0,0 +1,10 @@
1
+ import "react-native";
2
+
3
+ declare module "react-native" {
4
+ interface ViewProps {
5
+ dataSet?: Record<string, string | number | boolean | undefined>;
6
+ }
7
+ interface TextProps {
8
+ dataSet?: Record<string, string | number | boolean | undefined>;
9
+ }
10
+ }