@herca/rn-kit 0.1.68 → 0.1.70

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 (73) hide show
  1. package/lib/module/DatePicker/TimePicker.js +26 -46
  2. package/lib/module/DatePicker/TimePicker.js.map +1 -1
  3. package/lib/module/DatePicker/partials/AnimatedItem.js +52 -0
  4. package/lib/module/DatePicker/partials/AnimatedItem.js.map +1 -0
  5. package/lib/module/Input/InputFile.js +76 -275
  6. package/lib/module/Input/InputFile.js.map +1 -1
  7. package/lib/module/Input/README_INPUT_FILE.md +144 -23
  8. package/lib/module/Input/TextArea.js +5 -2
  9. package/lib/module/Input/TextArea.js.map +1 -1
  10. package/lib/module/Input/partials/InputFile/CardTrigger.js +77 -0
  11. package/lib/module/Input/partials/InputFile/CardTrigger.js.map +1 -0
  12. package/lib/module/Input/partials/InputFile/CardTriggerSmall.js +86 -0
  13. package/lib/module/Input/partials/InputFile/CardTriggerSmall.js.map +1 -0
  14. package/lib/module/Input/partials/InputFile/ItemPicker.js +43 -0
  15. package/lib/module/Input/partials/InputFile/ItemPicker.js.map +1 -0
  16. package/lib/module/Input/partials/InputFile/ItemPreview.js +149 -0
  17. package/lib/module/Input/partials/InputFile/ItemPreview.js.map +1 -0
  18. package/lib/module/Input/partials/InputFile/ModalDelete.js +76 -0
  19. package/lib/module/Input/partials/InputFile/ModalDelete.js.map +1 -0
  20. package/lib/module/Input/partials/InputFile/ModalPicker.js +72 -0
  21. package/lib/module/Input/partials/InputFile/ModalPicker.js.map +1 -0
  22. package/lib/module/LabelForm/LabelForm.js +3 -1
  23. package/lib/module/LabelForm/LabelForm.js.map +1 -1
  24. package/lib/module/Step/Step.js +1 -1
  25. package/lib/module/Step/StepItem.js +14 -6
  26. package/lib/module/Step/StepItem.js.map +1 -1
  27. package/lib/module/Ui/Center.js.map +1 -1
  28. package/lib/module/styles/layouting.js +3 -0
  29. package/lib/module/styles/layouting.js.map +1 -1
  30. package/lib/typescript/src/DatePicker/TimePicker.d.ts +4 -1
  31. package/lib/typescript/src/DatePicker/TimePicker.d.ts.map +1 -1
  32. package/lib/typescript/src/DatePicker/partials/AnimatedItem.d.ts +11 -0
  33. package/lib/typescript/src/DatePicker/partials/AnimatedItem.d.ts.map +1 -0
  34. package/lib/typescript/src/Input/InputFile.d.ts +17 -4
  35. package/lib/typescript/src/Input/InputFile.d.ts.map +1 -1
  36. package/lib/typescript/src/Input/TextArea.d.ts +1 -1
  37. package/lib/typescript/src/Input/TextArea.d.ts.map +1 -1
  38. package/lib/typescript/src/Input/partials/InputFile/CardTrigger.d.ts +11 -0
  39. package/lib/typescript/src/Input/partials/InputFile/CardTrigger.d.ts.map +1 -0
  40. package/lib/typescript/src/Input/partials/InputFile/CardTriggerSmall.d.ts +12 -0
  41. package/lib/typescript/src/Input/partials/InputFile/CardTriggerSmall.d.ts.map +1 -0
  42. package/lib/typescript/src/Input/partials/InputFile/ItemPicker.d.ts +10 -0
  43. package/lib/typescript/src/Input/partials/InputFile/ItemPicker.d.ts.map +1 -0
  44. package/lib/typescript/src/Input/partials/InputFile/ItemPreview.d.ts +11 -0
  45. package/lib/typescript/src/Input/partials/InputFile/ItemPreview.d.ts.map +1 -0
  46. package/lib/typescript/src/Input/partials/InputFile/ModalDelete.d.ts +12 -0
  47. package/lib/typescript/src/Input/partials/InputFile/ModalDelete.d.ts.map +1 -0
  48. package/lib/typescript/src/Input/partials/InputFile/ModalPicker.d.ts +18 -0
  49. package/lib/typescript/src/Input/partials/InputFile/ModalPicker.d.ts.map +1 -0
  50. package/lib/typescript/src/LabelForm/LabelForm.d.ts +2 -1
  51. package/lib/typescript/src/LabelForm/LabelForm.d.ts.map +1 -1
  52. package/lib/typescript/src/Step/StepItem.d.ts.map +1 -1
  53. package/lib/typescript/src/Ui/Center.d.ts +2 -2
  54. package/lib/typescript/src/Ui/Center.d.ts.map +1 -1
  55. package/lib/typescript/src/styles/layouting.d.ts +3 -0
  56. package/lib/typescript/src/styles/layouting.d.ts.map +1 -1
  57. package/package.json +1 -1
  58. package/src/DatePicker/TimePicker.tsx +37 -74
  59. package/src/DatePicker/partials/AnimatedItem.tsx +62 -0
  60. package/src/Input/InputFile.tsx +148 -296
  61. package/src/Input/README_INPUT_FILE.md +144 -23
  62. package/src/Input/TextArea.tsx +3 -0
  63. package/src/Input/partials/InputFile/CardTrigger.tsx +87 -0
  64. package/src/Input/partials/InputFile/CardTriggerSmall.tsx +120 -0
  65. package/src/Input/partials/InputFile/ItemPicker.tsx +46 -0
  66. package/src/Input/partials/InputFile/ItemPreview.tsx +182 -0
  67. package/src/Input/partials/InputFile/ModalDelete.tsx +84 -0
  68. package/src/Input/partials/InputFile/ModalPicker.tsx +86 -0
  69. package/src/LabelForm/LabelForm.tsx +7 -2
  70. package/src/Step/Step.tsx +1 -1
  71. package/src/Step/StepItem.tsx +15 -3
  72. package/src/Ui/Center.tsx +2 -2
  73. package/src/styles/layouting.ts +1 -0
@@ -11,7 +11,8 @@ Mendukung **camera**, **gallery**, dan **document picker** dengan tampilan previ
11
11
  - Preview file (gambar atau dokumen).
12
12
  - Mendukung **multiple file upload**.
13
13
  - Ganti file (replace) atau hapus file dengan konfirmasi.
14
- - Tambahkan label/nama dokumen secara manual.
14
+ - Tambahkan label/nama dokumen secara manual (hanya untuk variant `default`).
15
+ - **2 Variant tampilan**: `default` (full) dan `small` (compact).
15
16
  - Konfigurasi teks modal (custom title, description, button).
16
17
  - Callback `onChange` untuk mengirim data file terbaru ke parent.
17
18
 
@@ -29,6 +30,8 @@ Komponen ini membutuhkan beberapa library:
29
30
 
30
31
  ## 🚀 Penggunaan
31
32
 
33
+ ### Variant Default (Full Features)
34
+
32
35
  ```tsx
33
36
  import InputFile from '@/components/Input/InputFile';
34
37
  import { useState } from 'react';
@@ -38,12 +41,19 @@ export default function ExampleScreen() {
38
41
 
39
42
  return (
40
43
  <InputFile
44
+ variant="default" // atau bisa dihilangkan (default)
41
45
  title="Upload Dokumen"
42
46
  description="Format file: JPG, PNG, PDF"
47
+ hint="Maksimal ukuran file 5MB"
48
+ hasError={false}
43
49
  btnChooseFileText="Pilih File"
44
50
  multiple={true}
45
51
  value={files}
46
52
  useChangeLabel={true}
53
+ changeLableProps={{
54
+ label: 'Nama Dokumen',
55
+ placeholder: 'Masukkan nama dokumen',
56
+ }}
47
57
  modalPickFileText={{
48
58
  title: 'Pilih Sumber',
49
59
  description: 'Unggah dokumen atau ambil foto',
@@ -68,23 +78,67 @@ export default function ExampleScreen() {
68
78
  }
69
79
  ```
70
80
 
81
+ ### Variant Small (Compact)
82
+
83
+ ```tsx
84
+ import InputFile from '@/components/Input/InputFile';
85
+ import { useState } from 'react';
86
+
87
+ export default function ExampleScreen() {
88
+ const [files, setFiles] = useState<any[]>([]);
89
+
90
+ return (
91
+ <InputFile
92
+ variant="small"
93
+ title="Upload KTP"
94
+ btnChooseFileText="Pilih"
95
+ multiple={false}
96
+ value={files}
97
+ hasError={false}
98
+ onChange={(newFiles) => setFiles(newFiles)}
99
+ // ❌ description, hint, useChangeLabel, changeLableProps TIDAK BISA dipakai di variant small
100
+ />
101
+ );
102
+ }
103
+ ```
104
+
71
105
  ---
72
106
 
73
107
  ## ⚙️ Props
74
108
 
75
- | Prop | Tipe | Default | Deskripsi |
76
- | ------------------- | --------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------ |
77
- | `title` | `string` | `"Upload File"` | Judul input file. |
78
- | `description` | `string` | `"File harus berformat JPG, PNG dan PDF"` | Deskripsi singkat tentang file yang diterima. |
79
- | `accept` | `string[]` | `["application/pdf", "image/jpeg", "image/png"]` | Tipe MIME file yang diizinkan. |
80
- | `multiple` | `boolean` | `false` | Jika `true`, memungkinkan memilih banyak file sekaligus. |
81
- | `value` | `any[]` | `[]` | Daftar file yang sudah dipilih (state dari parent). |
82
- | `onChange` | `(files: any[]) => void` | `undefined` | Callback saat daftar file berubah (add, replace, delete). |
83
- | `btnChooseFileText` | `string` | `"Choose File"` | Label tombol untuk memilih file. |
84
- | `modalPickFileText` | `ModalPickFileText` (lihat bawah) | `undefined` | Teks kustom pada modal pemilihan sumber file. |
85
- | `modalDeleteText` | `ModalOption & confirmBtn` | `undefined` | Teks kustom pada modal konfirmasi hapus file. |
86
- | `useChangeLabel` | `boolean` | `false` | Jika `true`, menampilkan input untuk memberi nama/label pada file. |
87
- | `changeLableProps` | `ChangeLabelProps` (lihat bawah) | `undefined` | Teks kustom pada input text diatas preview file untuk |
109
+ ### Base Props (Tersedia di Semua Variant)
110
+
111
+ | Prop | Tipe | Default | Deskripsi |
112
+ | ------------------- | --------------------------------- | ------------------------------------------------ | --------------------------------------------------------- |
113
+ | `title` | `string` | `"Upload File"` | Judul input file. |
114
+ | `accept` | `string[]` | `["application/pdf", "image/jpeg", "image/png"]` | Tipe MIME file yang diizinkan. |
115
+ | `multiple` | `boolean` | `false` | Jika `true`, memungkinkan memilih banyak file sekaligus. |
116
+ | `value` | `any[]` | `[]` | Daftar file yang sudah dipilih (state dari parent). |
117
+ | `onChange` | `(files: any[]) => void` | `undefined` | Callback saat daftar file berubah (add, replace, delete). |
118
+ | `btnChooseFileText` | `string` | `"Choose File"` | Label tombol untuk memilih file. |
119
+ | `modalPickFileText` | `ModalPickFileText` (lihat bawah) | `undefined` | Teks kustom pada modal pemilihan sumber file. |
120
+ | `modalDeleteText` | `ModalOption & confirmBtn` | `undefined` | Teks kustom pada modal konfirmasi hapus file. |
121
+ | `hasError` | `boolean` | `false` | Menampilkan state error pada input. |
122
+
123
+ ### Variant-Specific Props
124
+
125
+ #### Default Variant (`variant?: 'default'`)
126
+
127
+ | Prop | Tipe | Default | Deskripsi |
128
+ | ------------------ | -------------------------------- | ----------------------------------------- | ------------------------------------------------------------ |
129
+ | `variant` | `'default'` | `'default'` | Variant default dengan fitur lengkap. |
130
+ | `description` | `string` | `"File harus berformat JPG, PNG dan PDF"` | Deskripsi singkat tentang file yang diterima. |
131
+ | `hint` | `string` | `undefined` | Teks hint tambahan di bawah input. |
132
+ | `useChangeLabel` | `boolean` | `false` | Jika `true`, menampilkan input untuk memberi nama pada file. |
133
+ | `changeLableProps` | `ChangeLabelProps` (lihat bawah) | `undefined` | Teks kustom pada input label di atas preview file. |
134
+
135
+ #### Small Variant (`variant: 'small'`)
136
+
137
+ | Prop | Tipe | Default | Deskripsi |
138
+ | --------- | --------- | ------- | ------------------------------------- |
139
+ | `variant` | `'small'` | - | Variant compact tanpa fitur tambahan. |
140
+
141
+ > ⚠️ **Catatan**: Props `description`, `hint`, `useChangeLabel`, dan `changeLableProps` **TIDAK TERSEDIA** di variant `small`. TypeScript akan menampilkan error jika props ini digunakan dengan `variant: 'small'`.
88
142
 
89
143
  ---
90
144
 
@@ -99,7 +153,7 @@ interface ModalOption {
99
153
  }
100
154
  ```
101
155
 
102
- ### `modalPickFileText`
156
+ ### `ModalPickFileText`
103
157
 
104
158
  ```ts
105
159
  interface ModalPickFileText {
@@ -111,20 +165,18 @@ interface ModalPickFileText {
111
165
  }
112
166
  ```
113
167
 
114
- ### `modalDeleteText`
168
+ ### `ModalDeleteText`
115
169
 
116
170
  ```ts
117
- interface ModalOption {
118
- title?: string;
119
- description?: string;
171
+ type ModalDeleteText = ModalOption & {
120
172
  confirmBtn?: {
121
173
  confirm?: string;
122
174
  cancel?: string;
123
175
  };
124
- }
176
+ };
125
177
  ```
126
178
 
127
- ### `changeLabelProps`
179
+ ### `ChangeLabelProps`
128
180
 
129
181
  ```ts
130
182
  interface ChangeLabelProps {
@@ -135,15 +187,81 @@ interface ChangeLabelProps {
135
187
 
136
188
  ---
137
189
 
138
- ## 📸 Preview UI
190
+ ## 📐 Type Definitions
191
+
192
+ ```ts
193
+ export interface ModalOption {
194
+ title?: string;
195
+ description?: string;
196
+ }
197
+
198
+ export interface ModalPickFileText {
199
+ title?: string;
200
+ description?: string;
201
+ camera?: ModalOption;
202
+ gallery?: ModalOption;
203
+ document?: ModalOption;
204
+ }
205
+
206
+ export interface ChangeLabelProps {
207
+ label?: string;
208
+ placeholder?: string;
209
+ }
210
+
211
+ type BaseInputFileProps = {
212
+ title?: string;
213
+ accept?: string[];
214
+ multiple?: boolean;
215
+ onChange?: (files: any) => void;
216
+ value?: any[];
217
+ modalPickFileText?: ModalPickFileText;
218
+ btnChooseFileText?: string;
219
+ modalDeleteText?: ModalOption & {
220
+ confirmBtn?: {
221
+ confirm?: string;
222
+ cancel?: string;
223
+ };
224
+ };
225
+ hasError?: boolean;
226
+ };
227
+
228
+ type DefaultVariantProps = BaseInputFileProps & {
229
+ variant?: 'default';
230
+ description?: string;
231
+ useChangeLabel?: boolean;
232
+ changeLableProps?: ChangeLabelProps;
233
+ hint?: string;
234
+ };
235
+
236
+ type SmallVariantProps = BaseInputFileProps & {
237
+ variant: 'small';
238
+ description?: never;
239
+ useChangeLabel?: never;
240
+ changeLableProps?: never;
241
+ hint?: never;
242
+ };
243
+
244
+ type InputFileProps = DefaultVariantProps | SmallVariantProps;
245
+ ```
246
+
247
+ ---
248
+
249
+ ### Variant Default
139
250
 
140
- - **Card input awal** → tombol pilih file + icon ilustrasi.
251
+ - **Card input awal** → tombol pilih file + icon ilustrasi + description + hint.
141
252
  - **Preview file** → menampilkan nama, ukuran, icon (atau thumbnail jika image).
253
+ - **Input label** (jika `useChangeLabel: true`) → input text untuk memberi nama dokumen.
142
254
  - **Aksi file**:
143
255
  - 🔄 Replace
144
256
  - ❌ Delete
145
257
  - 👁 Preview (buka dokumen dengan `viewDocument`)
146
258
 
259
+ ### Variant Small
260
+
261
+ - **Compact card** → tombol pilih file dengan tampilan lebih ringkas.
262
+ - **Preview file** → tampilan minimal tanpa label/hint tambahan.
263
+ - **Aksi file**: sama seperti variant default (replace, delete, preview).
264
+
147
265
  ---
148
266
 
149
267
  ## 🛠 Catatan
@@ -151,3 +269,6 @@ interface ChangeLabelProps {
151
269
  1. Pastikan permission kamera & storage sudah diatur (Android/iOS).
152
270
  2. File hasil picker akan memiliki property standar (`uri`, `type`, `name`, `size`).
153
271
  3. Untuk **multiple upload**, `value` berupa array file yang akan dikelola oleh parent.
272
+ 4. **Variant `small`** cocok untuk form yang membutuhkan tampilan compact tanpa deskripsi dan hint.
273
+ 5. **Variant `default`** cocok untuk form yang membutuhkan instruksi detail dan fitur labeling dokumen.
274
+ 6. TypeScript akan memberikan error compile-time jika props yang tidak sesuai variant digunakan.
@@ -11,6 +11,7 @@ export default function TextArea({
11
11
  style,
12
12
  hint,
13
13
  onChangeText,
14
+ ...props
14
15
  }: TextAreaProps) {
15
16
  const handleOnChangeText = (value: string) => {
16
17
  onChangeText?.(value);
@@ -36,6 +37,8 @@ export default function TextArea({
36
37
  numberOfLines={14}
37
38
  style={[styles.input, style]}
38
39
  textAlignVertical="top"
40
+ placeholderTextColor={Color.gray[500]}
41
+ {...props}
39
42
  />
40
43
  </View>
41
44
  {hint ? (
@@ -0,0 +1,87 @@
1
+ import { Image, StyleSheet, View } from 'react-native';
2
+ import Button from '../../../Button/Button';
3
+ import Typography from '../../../Typography/Typography';
4
+ import Card from '../../../Ui/Card';
5
+ import Center from '../../../Ui/Center';
6
+ import Color from '../../../Color/Color';
7
+ import { spacing } from '../../../styles/spacing';
8
+
9
+ interface Props {
10
+ title: string;
11
+ description: string;
12
+ btnSelect?: string;
13
+ onPress?: () => void;
14
+ hasError?: boolean;
15
+ hint?: string;
16
+ }
17
+
18
+ export default function CardTrigger({
19
+ title,
20
+ description,
21
+ btnSelect,
22
+ onPress,
23
+ hint,
24
+ hasError,
25
+ }: Props) {
26
+ return (
27
+ <Card style={styles.containerInput}>
28
+ <Center style={styles.centerInput}>
29
+ <Image source={require('../../assets/input-file.png')} />
30
+ <View style={spacing.gap[4]}>
31
+ <Typography
32
+ variant="t1"
33
+ weight="semibold"
34
+ color={Color.gray[500]}
35
+ center
36
+ >
37
+ {title}
38
+ </Typography>
39
+ {hint && (
40
+ <Typography
41
+ variant="t3"
42
+ weight="regular"
43
+ color={hasError ? Color.danger[500] : Color.gray[500]}
44
+ center
45
+ >
46
+ {hint}
47
+ </Typography>
48
+ )}
49
+ <Typography variant="t3" weight="regular" color={Color.gray[500]}>
50
+ {description}
51
+ </Typography>
52
+ </View>
53
+ <Button
54
+ color="primary"
55
+ title={btnSelect}
56
+ size="small"
57
+ onPress={onPress}
58
+ />
59
+ </Center>
60
+ </Card>
61
+ );
62
+ }
63
+
64
+ const styles = StyleSheet.create({
65
+ wrapper: {
66
+ gap: 12,
67
+ },
68
+ containerInput: {
69
+ borderStyle: 'dashed',
70
+ backgroundColor: Color.gray[50],
71
+ borderRadius: 16,
72
+ },
73
+ centerInput: {
74
+ gap: 14,
75
+ },
76
+ deleteFileContent: {
77
+ gap: 10,
78
+ },
79
+ deleteFileCenter: {
80
+ alignItems: 'center',
81
+ gap: 4,
82
+ },
83
+
84
+ previewWrapper: {
85
+ gap: 12,
86
+ },
87
+ });
@@ -0,0 +1,120 @@
1
+ import {
2
+ Image,
3
+ ScrollView,
4
+ StyleSheet,
5
+ TouchableOpacity,
6
+ View,
7
+ } from 'react-native';
8
+ import Card from '../../../Ui/Card';
9
+ import Color from '../../../Color/Color';
10
+ import Typography from '../../../Typography/Typography';
11
+ import Button from '../../../Button/Button';
12
+ import { layouting } from '../../../styles/layouting';
13
+ import ItemPreview from './ItemPreview';
14
+ import { spacing } from '../../../styles/spacing';
15
+
16
+ interface Props {
17
+ title?: string;
18
+ textButton?: string;
19
+ files: any[];
20
+ onChooseFile?: () => void;
21
+ onPreview?: (file: any) => void;
22
+ onReplace?: (index: number) => void;
23
+ onDelete?: (index: number) => void;
24
+ }
25
+
26
+ export default function CardTriggerSmall({
27
+ title = 'Upload File',
28
+ textButton = 'Choose File',
29
+ onChooseFile,
30
+ files,
31
+ onDelete,
32
+ onPreview,
33
+ onReplace,
34
+ }: Props) {
35
+ const Component = files?.length === 0 ? View : TouchableOpacity;
36
+
37
+ return (
38
+ <View style={[layouting.flex.row, spacing.gap[8]]}>
39
+ <Card
40
+ style={[styles.card, files.length === 0 ? layouting.flex.grow : {}]}
41
+ backgroundColor={Color?.gray[50]}
42
+ borderColor={Color?.gray[200]}
43
+ >
44
+ <Component
45
+ style={[
46
+ styles.titleWrapper,
47
+ files.length > 0 ? layouting.flex.col : {},
48
+ files.length > 0 ? spacing.gap[6] : {},
49
+ ]}
50
+ {...(files?.length > 0 && { onPress: onChooseFile })}
51
+ >
52
+ <Image
53
+ source={require('../../assets/input-file.png')}
54
+ style={files?.length === 0 ? styles.image : styles.imageSmall}
55
+ />
56
+ <Typography
57
+ variant={files?.length === 0 ? 't1' : 't3'}
58
+ weight={files?.length === 0 ? 'semibold' : 'regular'}
59
+ color={Color.gray[600]}
60
+ >
61
+ {title}
62
+ </Typography>
63
+ </Component>
64
+
65
+ {files.length === 0 && (
66
+ <Button
67
+ title={textButton}
68
+ color="primary"
69
+ size="small"
70
+ onPress={onChooseFile}
71
+ />
72
+ )}
73
+ </Card>
74
+
75
+ {files.length > 0 && (
76
+ <ScrollView
77
+ style={[layouting.flex[1]]}
78
+ contentContainerStyle={[spacing.gap[8], layouting.align.center]}
79
+ horizontal
80
+ >
81
+ {files.map((file, index) => (
82
+ <ItemPreview
83
+ variant="small"
84
+ key={index}
85
+ index={index}
86
+ file={file}
87
+ onPress={() => onPreview?.(file)}
88
+ onDelete={() => onDelete?.(index)}
89
+ onReplace={() => onReplace?.(index)}
90
+ />
91
+ ))}
92
+ </ScrollView>
93
+ )}
94
+ </View>
95
+ );
96
+ }
97
+
98
+ const styles = StyleSheet.create({
99
+ card: {
100
+ borderStyle: 'dashed',
101
+ flexDirection: 'row',
102
+ alignItems: 'center',
103
+ justifyContent: 'space-between',
104
+ gap: 8,
105
+ },
106
+ titleWrapper: {
107
+ flexDirection: 'row',
108
+ alignItems: 'center',
109
+ gap: 8,
110
+ flexShrink: 1,
111
+ },
112
+ image: {
113
+ width: 48,
114
+ height: 48,
115
+ },
116
+ imageSmall: {
117
+ width: 28,
118
+ height: 28,
119
+ },
120
+ });
@@ -0,0 +1,46 @@
1
+ import {
2
+ StyleSheet,
3
+ TouchableOpacity,
4
+ View,
5
+ type TouchableOpacityProps,
6
+ } from 'react-native';
7
+ import Icon, { type IconNameProps } from '../../../Icon';
8
+ import Typography from '../../../Typography/Typography';
9
+ import Color from '../../../Color/Color';
10
+
11
+ interface Props extends TouchableOpacityProps {
12
+ title: string;
13
+ description: string;
14
+ icon: IconNameProps;
15
+ }
16
+
17
+ export default function ItemPicker({
18
+ title,
19
+ description,
20
+ icon,
21
+ ...props
22
+ }: Props) {
23
+ return (
24
+ <TouchableOpacity {...props}>
25
+ <View style={styles.containerListItem}>
26
+ <Icon name={icon} color={Color.primary[1000]} />
27
+ <View>
28
+ <Typography variant="t2" weight="semibold" color={Color.gray[800]}>
29
+ {title}
30
+ </Typography>
31
+ <Typography variant="t2" color={Color.gray[600]}>
32
+ {description}
33
+ </Typography>
34
+ </View>
35
+ </View>
36
+ </TouchableOpacity>
37
+ );
38
+ }
39
+
40
+ const styles = StyleSheet.create({
41
+ containerListItem: {
42
+ flexDirection: 'row',
43
+ gap: 8,
44
+ alignItems: 'center',
45
+ },
46
+ });
@@ -0,0 +1,182 @@
1
+ import { Image, StyleSheet, TouchableOpacity, View } from 'react-native';
2
+ import Card from '../../../Ui/Card';
3
+ import Color from '../../../Color/Color';
4
+ import Typography from '../../../Typography/Typography';
5
+ import Icon from '../../../Icon';
6
+
7
+ interface Props {
8
+ file: any;
9
+ onPress: () => void;
10
+ index: number;
11
+ onReplace: (index: number) => void;
12
+ onDelete: (index: number) => void;
13
+ variant?: 'default' | 'small';
14
+ }
15
+
16
+ export default function ItemPreview({
17
+ file,
18
+ onPress,
19
+ index,
20
+ onDelete,
21
+ onReplace,
22
+ variant = 'default',
23
+ }: Props) {
24
+ const Wrapper = variant === 'default' ? Card : View;
25
+
26
+ return (
27
+ <Wrapper>
28
+ <TouchableOpacity
29
+ onPress={onPress}
30
+ style={styles.containerPreview}
31
+ activeOpacity={0.7}
32
+ >
33
+ <View style={styles.containerListItem}>
34
+ {file.type.startsWith('image') && (
35
+ <View
36
+ style={
37
+ (variant === 'small' ? styles.previewImageSmall : {},
38
+ variant === 'small' && file.error
39
+ ? styles.previewImageError
40
+ : {})
41
+ }
42
+ >
43
+ {variant === 'small' && (
44
+ <BtnIcon onPress={() => onDelete(index)} />
45
+ )}
46
+
47
+ <Image source={{ uri: file.uri }} style={styles.previewImage} />
48
+ </View>
49
+ )}
50
+
51
+ {!file.type.startsWith('image') && (
52
+ <View
53
+ style={[
54
+ styles.previewImage,
55
+ variant === 'small' ? styles.previewImageSmall : {},
56
+ variant === 'small' && file.error
57
+ ? styles.previewImageError
58
+ : {},
59
+ ]}
60
+ >
61
+ {variant === 'small' && (
62
+ <BtnIcon onPress={() => onDelete(index)} />
63
+ )}
64
+
65
+ <Icon
66
+ name={file.type === 'application/pdf' ? 'pdf-file' : 'Document'}
67
+ size={file.type === 'application/pdf' ? 32 : 24}
68
+ color={Color.primary[1000]}
69
+ />
70
+ </View>
71
+ )}
72
+
73
+ {variant === 'default' && (
74
+ <View style={styles.fileDetails}>
75
+ <Typography
76
+ variant="t1"
77
+ weight="semibold"
78
+ color={Color.gray[600]}
79
+ numberOfLines={1}
80
+ >
81
+ {file.name || `Image_${index + 1}`}
82
+ </Typography>
83
+
84
+ <Typography variant="t2" weight="regular" color={Color.gray[600]}>
85
+ {((file?.size ?? file?.fileSize ?? 0) / (1024 * 1024)).toFixed(
86
+ 2
87
+ )}
88
+ MB
89
+ </Typography>
90
+ </View>
91
+ )}
92
+ </View>
93
+
94
+ {/* action default */}
95
+ {variant === 'default' && (
96
+ <View style={styles.action}>
97
+ {file.error && (
98
+ <Icon
99
+ name="exclamation-triangle"
100
+ color={Color.danger[500]}
101
+ size={20}
102
+ />
103
+ )}
104
+
105
+ <TouchableOpacity onPress={() => onReplace(index)}>
106
+ <Icon name="rotate-right" size={18} color={Color.gray[700]} />
107
+ </TouchableOpacity>
108
+
109
+ <TouchableOpacity onPress={() => onDelete(index)}>
110
+ <Icon name="Times" size={12} color={Color.gray[700]} />
111
+ </TouchableOpacity>
112
+ </View>
113
+ )}
114
+ </TouchableOpacity>
115
+ </Wrapper>
116
+ );
117
+ }
118
+
119
+ const BtnIcon = ({ onPress }: { onPress: () => any }) => {
120
+ return (
121
+ <TouchableOpacity
122
+ style={styles.actionDelete}
123
+ onPress={onPress}
124
+ activeOpacity={0.7}
125
+ >
126
+ <Icon name="times-new" color="white" size={15} />
127
+ </TouchableOpacity>
128
+ );
129
+ };
130
+
131
+ const styles = StyleSheet.create({
132
+ containerListItem: {
133
+ flexDirection: 'row',
134
+ alignItems: 'center',
135
+ gap: 8,
136
+ flex: 1,
137
+ },
138
+ previewImage: {
139
+ width: 48,
140
+ height: 48,
141
+ borderRadius: 6,
142
+ alignItems: 'center',
143
+ justifyContent: 'center',
144
+ },
145
+ containerPreview: {
146
+ flexDirection: 'row',
147
+ justifyContent: 'space-between',
148
+ alignItems: 'center',
149
+ gap: 8,
150
+ },
151
+ fileDetails: {
152
+ justifyContent: 'center',
153
+ gap: 4,
154
+ flex: 1,
155
+ },
156
+ action: {
157
+ flexDirection: 'row',
158
+ alignItems: 'center',
159
+ gap: 12,
160
+ flexShrink: 0,
161
+ },
162
+
163
+ // small
164
+ previewImageSmall: {
165
+ borderWidth: 1,
166
+ borderColor: Color.gray[200],
167
+ borderRadius: 6,
168
+ position: 'relative',
169
+ },
170
+ previewImageError: {
171
+ borderColor: Color.danger[500],
172
+ },
173
+ actionDelete: {
174
+ position: 'absolute',
175
+ top: 1.5,
176
+ right: 1.5,
177
+ backgroundColor: Color.primary[1000],
178
+ zIndex: 1,
179
+ padding: 2,
180
+ borderRadius: 10,
181
+ },
182
+ });