@herca/rn-kit 0.1.69 → 0.1.71

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 (66) hide show
  1. package/lib/module/Icon/Icon.js +3 -1
  2. package/lib/module/Icon/Icon.js.map +1 -1
  3. package/lib/module/Icon/README.md +1 -0
  4. package/lib/module/Icon/icons/FaceId.js +61 -0
  5. package/lib/module/Icon/icons/FaceId.js.map +1 -0
  6. package/lib/module/Input/InputFile.js +76 -275
  7. package/lib/module/Input/InputFile.js.map +1 -1
  8. package/lib/module/Input/README_INPUT_FILE.md +144 -23
  9. package/lib/module/Input/partials/InputFile/CardTrigger.js +77 -0
  10. package/lib/module/Input/partials/InputFile/CardTrigger.js.map +1 -0
  11. package/lib/module/Input/partials/InputFile/CardTriggerSmall.js +86 -0
  12. package/lib/module/Input/partials/InputFile/CardTriggerSmall.js.map +1 -0
  13. package/lib/module/Input/partials/InputFile/ItemPicker.js +43 -0
  14. package/lib/module/Input/partials/InputFile/ItemPicker.js.map +1 -0
  15. package/lib/module/Input/partials/InputFile/ItemPreview.js +149 -0
  16. package/lib/module/Input/partials/InputFile/ItemPreview.js.map +1 -0
  17. package/lib/module/Input/partials/InputFile/ModalDelete.js +76 -0
  18. package/lib/module/Input/partials/InputFile/ModalDelete.js.map +1 -0
  19. package/lib/module/Input/partials/InputFile/ModalPicker.js +72 -0
  20. package/lib/module/Input/partials/InputFile/ModalPicker.js.map +1 -0
  21. package/lib/module/Spinner/Spinner.js +78 -0
  22. package/lib/module/Spinner/Spinner.js.map +1 -0
  23. package/lib/module/Ui/Center.js.map +1 -1
  24. package/lib/module/styles/layouting.js +3 -0
  25. package/lib/module/styles/layouting.js.map +1 -1
  26. package/lib/typescript/src/Icon/Icon.d.ts.map +1 -1
  27. package/lib/typescript/src/Icon/icons/FaceId.d.ts +9 -0
  28. package/lib/typescript/src/Icon/icons/FaceId.d.ts.map +1 -0
  29. package/lib/typescript/src/Icon/type.d.ts +1 -1
  30. package/lib/typescript/src/Icon/type.d.ts.map +1 -1
  31. package/lib/typescript/src/Input/InputFile.d.ts +17 -4
  32. package/lib/typescript/src/Input/InputFile.d.ts.map +1 -1
  33. package/lib/typescript/src/Input/partials/InputFile/CardTrigger.d.ts +11 -0
  34. package/lib/typescript/src/Input/partials/InputFile/CardTrigger.d.ts.map +1 -0
  35. package/lib/typescript/src/Input/partials/InputFile/CardTriggerSmall.d.ts +12 -0
  36. package/lib/typescript/src/Input/partials/InputFile/CardTriggerSmall.d.ts.map +1 -0
  37. package/lib/typescript/src/Input/partials/InputFile/ItemPicker.d.ts +10 -0
  38. package/lib/typescript/src/Input/partials/InputFile/ItemPicker.d.ts.map +1 -0
  39. package/lib/typescript/src/Input/partials/InputFile/ItemPreview.d.ts +11 -0
  40. package/lib/typescript/src/Input/partials/InputFile/ItemPreview.d.ts.map +1 -0
  41. package/lib/typescript/src/Input/partials/InputFile/ModalDelete.d.ts +12 -0
  42. package/lib/typescript/src/Input/partials/InputFile/ModalDelete.d.ts.map +1 -0
  43. package/lib/typescript/src/Input/partials/InputFile/ModalPicker.d.ts +18 -0
  44. package/lib/typescript/src/Input/partials/InputFile/ModalPicker.d.ts.map +1 -0
  45. package/lib/typescript/src/Spinner/Spinner.d.ts +5 -0
  46. package/lib/typescript/src/Spinner/Spinner.d.ts.map +1 -0
  47. package/lib/typescript/src/Ui/Center.d.ts +2 -2
  48. package/lib/typescript/src/Ui/Center.d.ts.map +1 -1
  49. package/lib/typescript/src/styles/layouting.d.ts +3 -0
  50. package/lib/typescript/src/styles/layouting.d.ts.map +1 -1
  51. package/package.json +1 -1
  52. package/src/Icon/Icon.tsx +2 -0
  53. package/src/Icon/README.md +1 -0
  54. package/src/Icon/icons/FaceId.tsx +64 -0
  55. package/src/Icon/type.tsx +2 -1
  56. package/src/Input/InputFile.tsx +148 -296
  57. package/src/Input/README_INPUT_FILE.md +144 -23
  58. package/src/Input/partials/InputFile/CardTrigger.tsx +87 -0
  59. package/src/Input/partials/InputFile/CardTriggerSmall.tsx +120 -0
  60. package/src/Input/partials/InputFile/ItemPicker.tsx +46 -0
  61. package/src/Input/partials/InputFile/ItemPreview.tsx +182 -0
  62. package/src/Input/partials/InputFile/ModalDelete.tsx +84 -0
  63. package/src/Input/partials/InputFile/ModalPicker.tsx +86 -0
  64. package/src/Spinner/Spinner.tsx +67 -0
  65. package/src/Ui/Center.tsx +2 -2
  66. package/src/styles/layouting.ts +1 -0
package/src/Icon/type.tsx CHANGED
@@ -107,7 +107,8 @@ export type IconNameProps =
107
107
  | 'times-new'
108
108
  | 'credit-card'
109
109
  | 'user-circle-money-up'
110
- | 'whatsapp-fill';
110
+ | 'whatsapp-fill'
111
+ | 'FaceId';
111
112
  export interface IconProps {
112
113
  name: IconNameProps;
113
114
  size?: number;
@@ -1,19 +1,18 @@
1
- import { useState, useEffect } from 'react';
2
- import { Image, StyleSheet, TouchableOpacity, View } from 'react-native';
3
- import BottomSheet from '../BottomSheet/BottomSheet';
4
- import Button from '../Button/Button';
1
+ import { useEffect, useState } from 'react';
2
+ import { View } from 'react-native';
5
3
  import Color from '../Color/Color';
6
- import List from '../List/List';
7
- import ListItem from '../List/ListItem';
8
4
  import Typography from '../Typography/Typography';
9
- import Card from '../Ui/Card';
10
- import Center from '../Ui/Center';
11
5
 
12
6
  import { pick } from '@react-native-documents/picker';
13
7
  import { viewDocument } from '@react-native-documents/viewer';
14
8
  import { launchCamera, launchImageLibrary } from 'react-native-image-picker';
15
- import Icon from '../Icon';
9
+ import { spacing } from '../styles/spacing';
16
10
  import Input from './Input';
11
+ import CardTrigger from './partials/InputFile/CardTrigger';
12
+ import ItemPreview from './partials/InputFile/ItemPreview';
13
+ import ModalDelete from './partials/InputFile/ModalDelete';
14
+ import ModalPicker from './partials/InputFile/ModalPicker';
15
+ import CardTriggerSmall from './partials/InputFile/CardTriggerSmall';
17
16
 
18
17
  export interface ModalOption {
19
18
  title?: string;
@@ -32,15 +31,13 @@ export interface ChangeLabelProps {
32
31
  placeholder?: string;
33
32
  }
34
33
 
35
- type InputFileProps = {
34
+ type BaseInputFileProps = {
36
35
  title?: string;
37
- description?: string;
38
36
  accept?: string[];
39
37
  multiple?: boolean;
40
38
  onChange?: (files: any) => void;
41
39
  value?: any[];
42
40
  modalPickFileText?: ModalPickFileText;
43
- useChangeLabel?: boolean;
44
41
  btnChooseFileText?: string;
45
42
  modalDeleteText?: ModalOption & {
46
43
  confirmBtn?: {
@@ -48,9 +45,28 @@ type InputFileProps = {
48
45
  cancel?: string;
49
46
  };
50
47
  };
48
+ hasError?: boolean;
49
+ };
50
+
51
+ type DefaultVariantProps = BaseInputFileProps & {
52
+ variant?: 'default';
53
+ description?: string;
54
+ useChangeLabel?: boolean;
51
55
  changeLableProps?: ChangeLabelProps;
56
+ hint?: string;
57
+ };
58
+
59
+ type SmallVariantProps = BaseInputFileProps & {
60
+ variant: 'small';
61
+ description?: never;
62
+ useChangeLabel?: never;
63
+ changeLableProps?: never;
64
+ hint?: never;
52
65
  };
53
66
 
67
+ // Union type
68
+ type InputFileProps = DefaultVariantProps | SmallVariantProps;
69
+
54
70
  export default function InputFile({
55
71
  title = 'Upload File',
56
72
  description = 'File harus berformat JPG, PNG dan PDF',
@@ -62,6 +78,9 @@ export default function InputFile({
62
78
  modalDeleteText,
63
79
  useChangeLabel = false,
64
80
  changeLableProps,
81
+ variant = 'default',
82
+ hint,
83
+ hasError,
65
84
  onChange,
66
85
  }: InputFileProps) {
67
86
  const [isOpenBottomSheetTypeFile, setIsOpenBottomSheetTypeFile] =
@@ -199,301 +218,134 @@ export default function InputFile({
199
218
  }, [value]);
200
219
 
201
220
  return (
202
- <View style={styles.wrapper}>
203
- <Card style={styles.containerInput}>
204
- <Center style={styles.centerInput}>
205
- <Image source={require('./assets/input-file.png')} />
206
- <Typography variant="t1" weight="semibold" color={Color.gray[500]}>
207
- {title}
208
- </Typography>
209
- <Typography variant="t3" weight="regular" color={Color.gray[500]}>
210
- {description}
211
- </Typography>
212
- <Button
213
- color="primary"
214
- title={btnChooseFileText}
215
- size="small"
216
- onPress={handleOnPresChoseFile}
217
- />
218
- </Center>
219
- </Card>
221
+ <View style={spacing.gap[12]}>
222
+ {variant === 'default' && (
223
+ <CardTrigger
224
+ title={title}
225
+ hint={hint}
226
+ hasError={hasError}
227
+ description={description}
228
+ btnSelect={btnChooseFileText}
229
+ onPress={handleOnPresChoseFile}
230
+ />
231
+ )}
220
232
 
221
- {files.map((file, index) => (
222
- <View style={styles.previewWrapper} key={index}>
223
- {useChangeLabel && (
224
- <Input
225
- label={`${changeLableProps?.label || 'Nama Dokumen'} ${index + 1}`}
226
- placeholder={
227
- changeLableProps?.placeholder || 'Masukan Nama Dokumen'
228
- }
229
- value={file.labelFile}
230
- onChangeText={(text) => {
231
- const updatedFiles = files.map((f, i) =>
232
- i === index ? { ...f, labelFile: text } : f
233
- );
234
- setFiles(updatedFiles);
235
- onChange?.(updatedFiles);
236
- }}
237
- />
233
+ {variant === 'small' && (
234
+ <View style={spacing.gap[4]}>
235
+ <CardTriggerSmall
236
+ files={files}
237
+ title={title}
238
+ textButton={btnChooseFileText}
239
+ onChooseFile={handleOnPresChoseFile}
240
+ onPreview={handlePreviewFile}
241
+ onReplace={handleReplaceFile}
242
+ onDelete={confirmDeleteFile}
243
+ />
244
+ {files.length > 0 && (
245
+ <>
246
+ {files
247
+ .filter((file) => !!file.error)
248
+ .map((file, index) => (
249
+ <Typography
250
+ key={index}
251
+ variant="t3"
252
+ color={Color.danger[500]}
253
+ >
254
+ {file.hint}
255
+ </Typography>
256
+ ))}
257
+ </>
238
258
  )}
239
- <View style={styles.gap4}>
240
- <Card>
241
- <TouchableOpacity onPress={() => handlePreviewFile(file)}>
242
- <View style={styles.containerPreview}>
243
- <View style={[styles.containerListItem, styles.flex1]}>
244
- {file.type.startsWith('image') ? (
245
- <Image
246
- source={
247
- file.uri?.endsWith('.pdf')
248
- ? require('../Input/assets/input-file.png')
249
- : { uri: file.uri }
250
- }
251
- style={styles.previewImage}
252
- />
253
- ) : (
254
- <View style={styles.previewImage}>
255
- <Icon name="Document" color={Color.primary[1000]} />
256
- </View>
257
- )}
258
- <View style={styles.fileDetails}>
259
- <Typography
260
- variant="t1"
261
- weight="semibold"
262
- color={Color.gray[600]}
263
- numberOfLines={1}
264
- >
265
- {file.name || `Image_${index + 1}`}
266
- </Typography>
267
- <Typography
268
- variant="t2"
269
- weight="regular"
270
- color={Color.gray[600]}
271
- >
272
- {(
273
- (file?.size ?? file?.fileSize ?? 0) /
274
- (1024 * 1024)
275
- ).toFixed(2)}
276
- MB
277
- </Typography>
278
- </View>
279
- </View>
280
- <View style={styles.action}>
281
- {file.error && (
282
- <Icon
283
- name="exclamation-triangle"
284
- color={Color.danger[500]}
285
- size={20}
286
- />
287
- )}
288
- <TouchableOpacity onPress={() => handleReplaceFile(index)}>
289
- <Icon
290
- name="rotate-right"
291
- size={18}
292
- color={Color.gray[700]}
293
- />
294
- </TouchableOpacity>
295
- <TouchableOpacity onPress={() => confirmDeleteFile(index)}>
296
- <Icon name="Times" size={12} color={Color.gray[700]} />
297
- </TouchableOpacity>
298
- </View>
299
- </View>
300
- </TouchableOpacity>
301
- </Card>
259
+ </View>
260
+ )}
302
261
 
303
- {file.hint && (
304
- <Typography
305
- variant="t3"
306
- color={!file.error ? Color.gray[700] : Color.danger[500]}
307
- >
308
- {file.hint}
309
- </Typography>
262
+ {variant === 'default' &&
263
+ files.map((file, index) => (
264
+ <View style={spacing.gap[12]} key={index}>
265
+ {useChangeLabel && (
266
+ <Input
267
+ label={`${changeLableProps?.label || 'Nama Dokumen'} ${index + 1}`}
268
+ value={file.labelFile}
269
+ placeholder={
270
+ changeLableProps?.placeholder || 'Masukan Nama Dokumen'
271
+ }
272
+ onChangeText={(text) => {
273
+ const updatedFiles = files.map((f, i) =>
274
+ i === index ? { ...f, labelFile: text } : f
275
+ );
276
+ setFiles(updatedFiles);
277
+ onChange?.(updatedFiles);
278
+ }}
279
+ />
310
280
  )}
281
+
282
+ <View style={spacing.gap[4]}>
283
+ <ItemPreview
284
+ index={index}
285
+ file={file}
286
+ onPress={() => handlePreviewFile(file)}
287
+ onReplace={() => handleReplaceFile(index)}
288
+ onDelete={() => confirmDeleteFile(index)}
289
+ />
290
+
291
+ {file.hint && (
292
+ <Typography
293
+ variant="t3"
294
+ color={!file.error ? Color.gray[700] : Color.danger[500]}
295
+ >
296
+ {file.hint}
297
+ </Typography>
298
+ )}
299
+ </View>
311
300
  </View>
312
- </View>
313
- ))}
301
+ ))}
314
302
 
315
- <BottomSheet
303
+ <ModalPicker
316
304
  isOpen={isOpenBottomSheetTypeFile}
317
305
  onClose={() => setIsOpenBottomSheetTypeFile(false)}
318
- >
319
- <View style={styles.bottomSheetContent}>
320
- <Center style={styles.bottomSheetCenter}>
321
- <Typography variant="p2" weight="semibold" color={Color.gray[800]}>
322
- {modalPickFileText?.title || 'Upload Dokumen'}
323
- </Typography>
324
- <Typography variant="t1" weight="regular" color={Color.gray[600]}>
325
- {modalPickFileText?.description || 'Unggah dokumen maksimal'}
326
- </Typography>
327
- </Center>
328
- <List>
329
- <ListItem>
330
- <TouchableOpacity onPress={handleOnPressButtonCamera}>
331
- <View style={styles.containerListItem}>
332
- <Icon name="Camera" color={Color.primary[1000]} />
333
- <View>
334
- <Typography
335
- variant="t2"
336
- weight="semibold"
337
- color={Color.gray[800]}
338
- >
339
- {modalPickFileText?.camera?.title || 'Ambil Foto'}
340
- </Typography>
341
- <Typography variant="t2" color={Color.gray[600]}>
342
- {modalPickFileText?.camera?.description ||
343
- 'Langsung ambil gambar dari kamera'}
344
- </Typography>
345
- </View>
346
- </View>
347
- </TouchableOpacity>
348
- </ListItem>
349
- <ListItem>
350
- <TouchableOpacity onPress={handleOnPressButtonGallery}>
351
- <View style={styles.containerListItem}>
352
- <Icon name="Image" color={Color.primary[1000]} />
353
- <View>
354
- <Typography
355
- variant="t2"
356
- weight="semibold"
357
- color={Color.gray[800]}
358
- >
359
- {modalPickFileText?.gallery?.title || 'Pilih dari Galeri'}
360
- </Typography>
361
- <Typography variant="t2" color={Color.gray[600]}>
362
- {modalPickFileText?.gallery?.description ||
363
- 'Cari gambar dari galeri perangkatmu'}
364
- </Typography>
365
- </View>
366
- </View>
367
- </TouchableOpacity>
368
- </ListItem>
369
- <ListItem>
370
- <TouchableOpacity onPress={handlePickFile}>
371
- <View style={styles.containerListItem}>
372
- <Icon name="Document" color={Color.primary[1000]} />
373
- <View>
374
- <Typography
375
- variant="t2"
376
- weight="semibold"
377
- color={Color.gray[800]}
378
- >
379
- {modalPickFileText?.document?.title || 'Pilih Dokumen'}
380
- </Typography>
381
- <Typography variant="t2" color={Color.gray[600]}>
382
- {modalPickFileText?.document?.description ||
383
- 'Pilih dokumen dari perangkatmu'}
384
- </Typography>
385
- </View>
386
- </View>
387
- </TouchableOpacity>
388
- </ListItem>
389
- </List>
390
- </View>
391
- </BottomSheet>
306
+ title={modalPickFileText?.title || 'Upload Dokumen'}
307
+ description={
308
+ modalPickFileText?.description || 'Unggah dokumen maksimal'
309
+ }
310
+ camera={{
311
+ icon: 'Camera',
312
+ title: modalPickFileText?.camera?.title || 'Ambil Foto',
313
+ onPress: handleOnPressButtonCamera,
314
+ description:
315
+ modalPickFileText?.camera?.description ||
316
+ 'Langsung ambil gambar dari kamera',
317
+ }}
318
+ gallery={{
319
+ icon: 'Image',
320
+ title: modalPickFileText?.gallery?.title || 'Pilih Dari Galeri',
321
+ onPress: handleOnPressButtonGallery,
322
+ description:
323
+ modalPickFileText?.gallery?.description ||
324
+ 'Cari gambar dari galeri perangkatmu',
325
+ }}
326
+ document={{
327
+ icon: 'Document',
328
+ title: modalPickFileText?.document?.title || 'Pilih Dokumen',
329
+ onPress: handlePickFile,
330
+ description:
331
+ modalPickFileText?.document?.description ||
332
+ 'Pilih dokumen dari perangkatmu',
333
+ }}
334
+ />
392
335
 
393
- <BottomSheet
336
+ <ModalDelete
394
337
  isOpen={isOpenBottomSheetDeleteFile}
395
338
  onClose={() => setIsOpenBottomSheetDeleteFile(false)}
396
- >
397
- <View style={styles.deleteFileContent}>
398
- <Center style={styles.deleteFileCenter}>
399
- <Typography
400
- variant="p2"
401
- weight="semibold"
402
- color={Color.gray[800]}
403
- center
404
- >
405
- {modalDeleteText?.title || 'Hapus Dokumen'}
406
- </Typography>
407
- <Typography
408
- variant="t1"
409
- weight="regular"
410
- color={Color.gray[500]}
411
- center
412
- >
413
- {modalDeleteText?.description ||
414
- 'Apakah kamu akan menghapus dokumen yang dipilih?'}
415
- </Typography>
416
- </Center>
417
- <View>
418
- <Button
419
- color="danger"
420
- title={modalDeleteText?.confirmBtn?.confirm || 'Hapus'}
421
- onPress={deleteFile}
422
- />
423
- <Button
424
- color="primary"
425
- variant="tertiary"
426
- title={modalDeleteText?.confirmBtn?.cancel || 'Batal'}
427
- onPress={() => setIsOpenBottomSheetDeleteFile(false)}
428
- />
429
- </View>
430
- </View>
431
- </BottomSheet>
339
+ title={modalDeleteText?.title || 'Hapus Dokumen'}
340
+ descripition={
341
+ modalDeleteText?.description ||
342
+ 'Apakah kamu akan menghapus dokumen yang dipilih?'
343
+ }
344
+ confirm={modalDeleteText?.confirmBtn?.confirm || 'Hapus'}
345
+ onConfirm={deleteFile}
346
+ cancel={modalDeleteText?.confirmBtn?.cancel || 'Batal'}
347
+ onCnacel={() => setIsOpenBottomSheetDeleteFile(false)}
348
+ />
432
349
  </View>
433
350
  );
434
351
  }
435
-
436
- const styles = StyleSheet.create({
437
- wrapper: {
438
- gap: 12,
439
- },
440
- containerInput: {
441
- borderStyle: 'dashed',
442
- backgroundColor: Color.gray[50],
443
- borderRadius: 16,
444
- },
445
- centerInput: {
446
- gap: 14,
447
- },
448
- containerPreview: {
449
- flexDirection: 'row',
450
- justifyContent: 'space-between',
451
- alignItems: 'center',
452
- gap: 8,
453
- },
454
- previewImage: {
455
- width: 44,
456
- height: 44,
457
- borderRadius: 6,
458
- alignItems: 'center',
459
- justifyContent: 'center',
460
- },
461
- fileDetails: {
462
- justifyContent: 'center',
463
- gap: 4,
464
- flex: 1,
465
- },
466
- containerListItem: {
467
- flexDirection: 'row',
468
- gap: 8,
469
- alignItems: 'center',
470
- },
471
- bottomSheetContent: {
472
- gap: 16,
473
- },
474
- bottomSheetCenter: {
475
- alignItems: 'center',
476
- },
477
- deleteFileContent: {
478
- gap: 10,
479
- },
480
- deleteFileCenter: {
481
- alignItems: 'center',
482
- gap: 4,
483
- },
484
- action: {
485
- flexDirection: 'row',
486
- alignItems: 'center',
487
- gap: 12,
488
- flexShrink: 0,
489
- },
490
- previewWrapper: {
491
- gap: 12,
492
- },
493
- flex1: {
494
- flex: 1,
495
- },
496
- gap4: {
497
- gap: 4,
498
- },
499
- });