@herca/rn-kit 0.1.100 → 0.1.102
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/lib/module/Icon/Icon.js +17 -1
- package/lib/module/Icon/Icon.js.map +1 -1
- package/lib/module/Icon/icons/BarCode.js +22 -0
- package/lib/module/Icon/icons/BarCode.js.map +1 -0
- package/lib/module/Icon/icons/BoxSearchOutline.js +22 -0
- package/lib/module/Icon/icons/BoxSearchOutline.js.map +1 -0
- package/lib/module/Icon/icons/CubeScan.js +34 -0
- package/lib/module/Icon/icons/CubeScan.js.map +1 -0
- package/lib/module/Icon/icons/Devices.js +27 -0
- package/lib/module/Icon/icons/Devices.js.map +1 -0
- package/lib/module/Icon/icons/Global.js +46 -0
- package/lib/module/Icon/icons/Global.js.map +1 -0
- package/lib/module/Icon/icons/Hierarchy3.js +31 -0
- package/lib/module/Icon/icons/Hierarchy3.js.map +1 -0
- package/lib/module/Icon/icons/ScanQr.js +22 -0
- package/lib/module/Icon/icons/ScanQr.js.map +1 -0
- package/lib/module/Icon/icons/UserSquareOutline.js +25 -0
- package/lib/module/Icon/icons/UserSquareOutline.js.map +1 -0
- package/lib/module/Input/InputFile.js +178 -68
- package/lib/module/Input/InputFile.js.map +1 -1
- package/lib/module/Input/README_INPUT_FILE.md +179 -105
- package/lib/module/Input/TextArea.js +2 -0
- package/lib/module/Input/TextArea.js.map +1 -1
- package/lib/module/Input/helpers/uploadFile.js +60 -0
- package/lib/module/Input/helpers/uploadFile.js.map +1 -0
- package/lib/module/Input/partials/InputFile/CardTriggerSmall.js +1 -0
- package/lib/module/Input/partials/InputFile/CardTriggerSmall.js.map +1 -1
- package/lib/module/Input/partials/InputFile/ItemPreview.js +15 -4
- package/lib/module/Input/partials/InputFile/ItemPreview.js.map +1 -1
- package/lib/module/Tab/Tab.js +3 -1
- package/lib/module/Tab/Tab.js.map +1 -1
- package/lib/typescript/src/Icon/Icon.d.ts.map +1 -1
- package/lib/typescript/src/Icon/icons/BarCode.d.ts +9 -0
- package/lib/typescript/src/Icon/icons/BarCode.d.ts.map +1 -0
- package/lib/typescript/src/Icon/icons/BoxSearchOutline.d.ts +9 -0
- package/lib/typescript/src/Icon/icons/BoxSearchOutline.d.ts.map +1 -0
- package/lib/typescript/src/Icon/icons/CubeScan.d.ts +9 -0
- package/lib/typescript/src/Icon/icons/CubeScan.d.ts.map +1 -0
- package/lib/typescript/src/Icon/icons/Devices.d.ts +8 -0
- package/lib/typescript/src/Icon/icons/Devices.d.ts.map +1 -0
- package/lib/typescript/src/Icon/icons/Global.d.ts +9 -0
- package/lib/typescript/src/Icon/icons/Global.d.ts.map +1 -0
- package/lib/typescript/src/Icon/icons/Hierarchy3.d.ts +9 -0
- package/lib/typescript/src/Icon/icons/Hierarchy3.d.ts.map +1 -0
- package/lib/typescript/src/Icon/icons/ScanQr.d.ts +9 -0
- package/lib/typescript/src/Icon/icons/ScanQr.d.ts.map +1 -0
- package/lib/typescript/src/Icon/icons/UserSquareOutline.d.ts +9 -0
- package/lib/typescript/src/Icon/icons/UserSquareOutline.d.ts.map +1 -0
- package/lib/typescript/src/Icon/type.d.ts +1 -1
- package/lib/typescript/src/Icon/type.d.ts.map +1 -1
- package/lib/typescript/src/Input/InputFile.d.ts +3 -48
- package/lib/typescript/src/Input/InputFile.d.ts.map +1 -1
- package/lib/typescript/src/Input/TextArea.d.ts +1 -1
- package/lib/typescript/src/Input/TextArea.d.ts.map +1 -1
- package/lib/typescript/src/Input/helpers/uploadFile.d.ts +3 -0
- package/lib/typescript/src/Input/helpers/uploadFile.d.ts.map +1 -0
- package/lib/typescript/src/Input/partials/InputFile/CardTriggerSmall.d.ts.map +1 -1
- package/lib/typescript/src/Input/partials/InputFile/ItemPreview.d.ts +4 -2
- package/lib/typescript/src/Input/partials/InputFile/ItemPreview.d.ts.map +1 -1
- package/lib/typescript/src/Input/type.d.ts +91 -0
- package/lib/typescript/src/Input/type.d.ts.map +1 -1
- package/lib/typescript/src/Tab/Tab.d.ts.map +1 -1
- package/lib/typescript/src/Tab/type.d.ts +2 -0
- package/lib/typescript/src/Tab/type.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Icon/Icon.tsx +16 -0
- package/src/Icon/icons/BarCode.tsx +19 -0
- package/src/Icon/icons/BoxSearchOutline.tsx +23 -0
- package/src/Icon/icons/CubeScan.tsx +35 -0
- package/src/Icon/icons/Devices.tsx +26 -0
- package/src/Icon/icons/Global.tsx +55 -0
- package/src/Icon/icons/Hierarchy3.tsx +31 -0
- package/src/Icon/icons/ScanQr.tsx +19 -0
- package/src/Icon/icons/UserSquareOutline.tsx +27 -0
- package/src/Icon/type.tsx +9 -1
- package/src/Input/InputFile.tsx +235 -111
- package/src/Input/README_INPUT_FILE.md +179 -105
- package/src/Input/TextArea.tsx +2 -1
- package/src/Input/helpers/uploadFile.ts +77 -0
- package/src/Input/partials/InputFile/CardTriggerSmall.tsx +1 -0
- package/src/Input/partials/InputFile/ItemPreview.tsx +25 -4
- package/src/Input/type.tsx +105 -0
- package/src/Tab/Tab.tsx +2 -0
- package/src/Tab/type.tsx +5 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Svg, { Path } from 'react-native-svg';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
size?: number;
|
|
6
|
+
color?: string;
|
|
7
|
+
style?: object;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const GlobalIcon: React.FC<Props> = ({
|
|
11
|
+
size = 24,
|
|
12
|
+
color = 'currentColor',
|
|
13
|
+
style,
|
|
14
|
+
}) => (
|
|
15
|
+
<Svg width={size} height={size} viewBox="0 0 20 20" fill="none" style={style}>
|
|
16
|
+
<Path
|
|
17
|
+
d="M6.37496 17.4243C6.34996 17.4243 6.31663 17.441 6.29163 17.441C4.67496 16.641 3.35829 15.316 2.54996 13.6993C2.54996 13.6743 2.56663 13.641 2.56663 13.616C3.58329 13.916 4.63329 14.141 5.67496 14.316C5.85829 15.366 6.07496 16.4077 6.37496 17.4243Z"
|
|
18
|
+
fill={color}
|
|
19
|
+
/>
|
|
20
|
+
<Path
|
|
21
|
+
d="M17.45 13.7077C16.625 15.366 15.25 16.7077 13.575 17.516C13.8916 16.4577 14.1583 15.391 14.3333 14.316C15.3833 14.141 16.4166 13.916 17.4333 13.616C17.425 13.6493 17.45 13.6827 17.45 13.7077Z"
|
|
22
|
+
fill={color}
|
|
23
|
+
/>
|
|
24
|
+
<Path
|
|
25
|
+
d="M17.5166 6.42435C16.4666 6.10768 15.4083 5.84935 14.3333 5.66602C14.1583 4.59102 13.9 3.52435 13.575 2.48268C15.3 3.30768 16.6916 4.69935 17.5166 6.42435Z"
|
|
26
|
+
fill={color}
|
|
27
|
+
/>
|
|
28
|
+
<Path
|
|
29
|
+
d="M6.37496 2.57435C6.07496 3.59102 5.85829 4.62435 5.68329 5.67435C4.60829 5.84102 3.54163 6.10768 2.48329 6.42435C3.29163 4.74935 4.63329 3.37435 6.29163 2.54935C6.31663 2.54935 6.34996 2.57435 6.37496 2.57435Z"
|
|
30
|
+
fill={color}
|
|
31
|
+
/>
|
|
32
|
+
<Path
|
|
33
|
+
d="M12.9083 5.49102C10.975 5.27435 9.02496 5.27435 7.09163 5.49102C7.29996 4.34935 7.56663 3.20768 7.94163 2.10768C7.95829 2.04102 7.94996 1.99102 7.95829 1.92435C8.61663 1.76602 9.29163 1.66602 9.99996 1.66602C10.7 1.66602 11.3833 1.76602 12.0333 1.92435C12.0416 1.99102 12.0416 2.04102 12.0583 2.10768C12.4333 3.21602 12.7 4.34935 12.9083 5.49102Z"
|
|
34
|
+
fill={color}
|
|
35
|
+
/>
|
|
36
|
+
<Path
|
|
37
|
+
d="M5.49163 12.9077C4.34163 12.6993 3.20829 12.4327 2.10829 12.0577C2.04163 12.041 1.99163 12.0493 1.92496 12.041C1.76663 11.3827 1.66663 10.7077 1.66663 9.99935C1.66663 9.29935 1.76663 8.61602 1.92496 7.96602C1.99163 7.95768 2.04163 7.95768 2.10829 7.94102C3.21663 7.57435 4.34163 7.29935 5.49163 7.09102C5.28329 9.02435 5.28329 10.9743 5.49163 12.9077Z"
|
|
38
|
+
fill={color}
|
|
39
|
+
/>
|
|
40
|
+
<Path
|
|
41
|
+
d="M18.3333 9.99935C18.3333 10.7077 18.2333 11.3827 18.075 12.041C18.0083 12.0493 17.9583 12.041 17.8916 12.0577C16.7833 12.4243 15.65 12.6993 14.5083 12.9077C14.725 10.9743 14.725 9.02435 14.5083 7.09102C15.65 7.29935 16.7916 7.56602 17.8916 7.94102C17.9583 7.95768 18.0083 7.96602 18.075 7.96602C18.2333 8.62435 18.3333 9.29935 18.3333 9.99935Z"
|
|
42
|
+
fill={color}
|
|
43
|
+
/>
|
|
44
|
+
<Path
|
|
45
|
+
d="M12.9083 14.5077C12.7 15.6577 12.4333 16.791 12.0583 17.891C12.0416 17.9577 12.0416 18.0077 12.0333 18.0743C11.3833 18.2327 10.7 18.3327 9.99996 18.3327C9.29163 18.3327 8.61663 18.2327 7.95829 18.0743C7.94996 18.0077 7.95829 17.9577 7.94163 17.891C7.57496 16.7827 7.29996 15.6577 7.09163 14.5077C8.05829 14.616 9.02496 14.691 9.99996 14.691C10.975 14.691 11.95 14.616 12.9083 14.5077Z"
|
|
46
|
+
fill={color}
|
|
47
|
+
/>
|
|
48
|
+
<Path
|
|
49
|
+
d="M13.1361 13.1355C11.0518 13.3984 8.9481 13.3984 6.86385 13.1355C6.60088 11.0512 6.60088 8.94749 6.86385 6.86324C8.9481 6.60027 11.0518 6.60027 13.1361 6.86324C13.399 8.94749 13.399 11.0512 13.1361 13.1355Z"
|
|
50
|
+
fill={color}
|
|
51
|
+
/>
|
|
52
|
+
</Svg>
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
export default GlobalIcon;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Svg, { Path } from 'react-native-svg';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
size?: number;
|
|
6
|
+
style?: object;
|
|
7
|
+
color?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const Hierarchy3: React.FC<Props> = ({ size = 24, style, color = '#000' }) => (
|
|
11
|
+
<Svg width={size} height={size} viewBox="0 0 20 20" fill="none" style={style}>
|
|
12
|
+
<Path
|
|
13
|
+
d="M4.1665 6.66699C5.54722 6.66699 6.6665 5.5477 6.6665 4.16699C6.6665 2.78628 5.54722 1.66699 4.1665 1.66699C2.78579 1.66699 1.6665 2.78628 1.6665 4.16699C1.6665 5.5477 2.78579 6.66699 4.1665 6.66699Z"
|
|
14
|
+
fill={color}
|
|
15
|
+
/>
|
|
16
|
+
<Path
|
|
17
|
+
d="M15.8332 12.5003C17.2139 12.5003 18.3332 11.381 18.3332 10.0003C18.3332 8.61961 17.2139 7.50033 15.8332 7.50033C14.4525 7.50033 13.3332 8.61961 13.3332 10.0003C13.3332 11.381 14.4525 12.5003 15.8332 12.5003Z"
|
|
18
|
+
fill={color}
|
|
19
|
+
/>
|
|
20
|
+
<Path
|
|
21
|
+
d="M4.1665 18.3337C5.54722 18.3337 6.6665 17.2144 6.6665 15.8337C6.6665 14.4529 5.54722 13.3337 4.1665 13.3337C2.78579 13.3337 1.6665 14.4529 1.6665 15.8337C1.6665 17.2144 2.78579 18.3337 4.1665 18.3337Z"
|
|
22
|
+
fill={color}
|
|
23
|
+
/>
|
|
24
|
+
<Path
|
|
25
|
+
d="M4.1665 13.9587C3.82484 13.9587 3.5415 13.6753 3.5415 13.3337V6.66699C3.5415 6.32533 3.82484 6.04199 4.1665 6.04199C4.50817 6.04199 4.7915 6.32533 4.7915 6.66699C4.7915 8.49199 5.67484 9.37533 7.49984 9.37533H13.3332C13.6748 9.37533 13.9582 9.65866 13.9582 10.0003C13.9582 10.342 13.6748 10.6253 13.3332 10.6253H7.49984C6.3665 10.6253 5.45817 10.3337 4.7915 9.78366V13.3337C4.7915 13.6753 4.50817 13.9587 4.1665 13.9587Z"
|
|
26
|
+
fill={color}
|
|
27
|
+
/>
|
|
28
|
+
</Svg>
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export default Hierarchy3;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Svg, { Path } from 'react-native-svg';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
size?: number;
|
|
6
|
+
color?: string;
|
|
7
|
+
style?: object;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const ScanQr: React.FC<Props> = ({ size = 24, color = '#000', style }) => (
|
|
11
|
+
<Svg width={size} height={size} viewBox="0 0 32 32" fill="none" style={style}>
|
|
12
|
+
<Path
|
|
13
|
+
d="M11.6667 28C11.6667 28.552 11.2187 29 10.6667 29H6C4.12133 29 3 27.8787 3 26V21.3333C3 20.7813 3.448 20.3333 4 20.3333C4.552 20.3333 5 20.7813 5 21.3333V26C5 26.7853 5.21467 27 6 27H10.6667C11.2187 27 11.6667 27.448 11.6667 28ZM4 11.6667C4.552 11.6667 5 11.2187 5 10.6667V6C5 5.21467 5.21467 5 6 5H10.6667C11.2187 5 11.6667 4.552 11.6667 4C11.6667 3.448 11.2187 3 10.6667 3H6C4.12133 3 3 4.12133 3 6V10.6667C3 11.2187 3.448 11.6667 4 11.6667ZM26 3H21.3333C20.7813 3 20.3333 3.448 20.3333 4C20.3333 4.552 20.7813 5 21.3333 5H26C26.7853 5 27 5.21467 27 6V10.6667C27 11.2187 27.448 11.6667 28 11.6667C28.552 11.6667 29 11.2187 29 10.6667V6C29 4.12133 27.8787 3 26 3ZM28 20.3333C27.448 20.3333 27 20.7813 27 21.3333V26C27 26.7853 26.7853 27 26 27H21.3333C20.7813 27 20.3333 27.448 20.3333 28C20.3333 28.552 20.7813 29 21.3333 29H26C27.8787 29 29 27.8787 29 26V21.3333C29 20.7813 28.552 20.3333 28 20.3333ZM18.3333 14.6667H21.6667C22.3333 14.6667 22.6667 14.3333 22.6667 13.6667V10.3333C22.6667 9.66667 22.3333 9.33333 21.6667 9.33333H18.3333C17.6667 9.33333 17.3333 9.66667 17.3333 10.3333V13.6667C17.3333 14.3333 17.6667 14.6667 18.3333 14.6667ZM13.6667 17.3333H10.3333C9.66667 17.3333 9.33333 17.6667 9.33333 18.3333V21.6667C9.33333 22.3333 9.66667 22.6667 10.3333 22.6667H13.6667C14.3333 22.6667 14.6667 22.3333 14.6667 21.6667V18.3333C14.6667 17.6667 14.3333 17.3333 13.6667 17.3333ZM22.6667 21.6667V18.3333C22.6667 17.6667 22.3333 17.3333 21.6667 17.3333H18.3333C17.6667 17.3333 17.3333 17.6667 17.3333 18.3333V21.6667C17.3333 22.3333 17.6667 22.6667 18.3333 22.6667H21.6667C22.3333 22.6667 22.6667 22.3333 22.6667 21.6667ZM13.6667 9.33333H10.3333C9.66667 9.33333 9.33333 9.66667 9.33333 10.3333V13.6667C9.33333 14.3333 9.66667 14.6667 10.3333 14.6667H13.6667C14.3333 14.6667 14.6667 14.3333 14.6667 13.6667V10.3333C14.6667 9.66667 14.3333 9.33333 13.6667 9.33333Z"
|
|
14
|
+
fill={color}
|
|
15
|
+
/>
|
|
16
|
+
</Svg>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
export default ScanQr;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Svg, { Path } from 'react-native-svg';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
size?: number;
|
|
6
|
+
color?: string;
|
|
7
|
+
style?: object;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const UserSquareOutline: React.FC<Props> = ({
|
|
11
|
+
size = 24,
|
|
12
|
+
color = '#909090',
|
|
13
|
+
style,
|
|
14
|
+
}) => (
|
|
15
|
+
<Svg width={size} height={size} viewBox="0 0 20 20" fill="none" style={style}>
|
|
16
|
+
<Path
|
|
17
|
+
d="M13.4915 1.66699H6.50817C3.47484 1.66699 1.6665 3.47533 1.6665 6.50866V13.492C1.6665 15.8337 2.7415 17.442 4.63317 18.0503C5.18317 18.242 5.8165 18.3337 6.50817 18.3337H13.4915C14.1832 18.3337 14.8165 18.242 15.3665 18.0503C17.2582 17.442 18.3332 15.8337 18.3332 13.492V6.50866C18.3332 3.47533 16.5248 1.66699 13.4915 1.66699ZM17.0832 13.492C17.0832 15.2753 16.3832 16.4003 14.9748 16.867C14.1665 15.2753 12.2498 14.142 9.99984 14.142C7.74984 14.142 5.8415 15.267 5.02484 16.867H5.0165C3.62484 16.417 2.9165 15.2837 2.9165 13.5003V6.50866C2.9165 4.15866 4.15817 2.91699 6.50817 2.91699H13.4915C15.8415 2.91699 17.0832 4.15866 17.0832 6.50866V13.492Z"
|
|
18
|
+
fill={color}
|
|
19
|
+
/>
|
|
20
|
+
<Path
|
|
21
|
+
d="M9.99984 6.66699C8.34984 6.66699 7.0165 8.00033 7.0165 9.65033C7.0165 11.3003 8.34984 12.642 9.99984 12.642C11.6498 12.642 12.9832 11.3003 12.9832 9.65033C12.9832 8.00033 11.6498 6.66699 9.99984 6.66699Z"
|
|
22
|
+
fill={color}
|
|
23
|
+
/>
|
|
24
|
+
</Svg>
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
export default UserSquareOutline;
|
package/src/Icon/type.tsx
CHANGED
|
@@ -145,7 +145,15 @@ export type IconNameProps =
|
|
|
145
145
|
| 'calendar-plus'
|
|
146
146
|
| 'calendar-clock'
|
|
147
147
|
| 'sort-vertical'
|
|
148
|
-
| 'warning-letter'
|
|
148
|
+
| 'warning-letter'
|
|
149
|
+
| 'bar-code'
|
|
150
|
+
| 'devices'
|
|
151
|
+
| 'user-square-outline'
|
|
152
|
+
| 'box-search-outline'
|
|
153
|
+
| 'hierarchy-3'
|
|
154
|
+
| 'global'
|
|
155
|
+
| 'scan-qr'
|
|
156
|
+
| 'cube-scan';
|
|
149
157
|
export interface IconProps {
|
|
150
158
|
name: IconNameProps;
|
|
151
159
|
size?: number;
|
package/src/Input/InputFile.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
1
|
+
import { useEffect, useState, useRef } from 'react';
|
|
2
2
|
import { View } from 'react-native';
|
|
3
3
|
import Color from '../Color/Color';
|
|
4
4
|
import Typography from '../Typography/Typography';
|
|
5
5
|
|
|
6
|
-
import { pick } from '@react-native-documents/picker';
|
|
6
|
+
import { pick, types } from '@react-native-documents/picker';
|
|
7
7
|
import { viewDocument } from '@react-native-documents/viewer';
|
|
8
8
|
import { launchCamera, launchImageLibrary } from 'react-native-image-picker';
|
|
9
9
|
import { spacing } from '../styles/spacing';
|
|
@@ -14,64 +14,24 @@ import ModalDelete from './partials/InputFile/ModalDelete';
|
|
|
14
14
|
import ModalPicker from './partials/InputFile/ModalPicker';
|
|
15
15
|
import CardTriggerSmall from './partials/InputFile/CardTriggerSmall';
|
|
16
16
|
import ReactNativeBlobUtil from 'react-native-blob-util';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
description?: string;
|
|
25
|
-
camera?: ModalOption;
|
|
26
|
-
gallery?: ModalOption;
|
|
27
|
-
document?: ModalOption;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface ChangeLabelProps {
|
|
31
|
-
label?: string;
|
|
32
|
-
placeholder?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
type BaseInputFileProps = {
|
|
36
|
-
title?: string;
|
|
37
|
-
accept?: string[];
|
|
38
|
-
multiple?: boolean;
|
|
39
|
-
onChange?: (files: any) => void;
|
|
40
|
-
value?: any[];
|
|
41
|
-
modalPickFileText?: ModalPickFileText;
|
|
42
|
-
btnChooseFileText?: string;
|
|
43
|
-
modalDeleteText?: ModalOption & {
|
|
44
|
-
confirmBtn?: {
|
|
45
|
-
confirm?: string;
|
|
46
|
-
cancel?: string;
|
|
47
|
-
};
|
|
48
|
-
};
|
|
49
|
-
hasError?: boolean;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
type DefaultVariantProps = BaseInputFileProps & {
|
|
53
|
-
variant?: 'default';
|
|
54
|
-
description?: string;
|
|
55
|
-
useChangeLabel?: boolean;
|
|
56
|
-
changeLableProps?: ChangeLabelProps;
|
|
57
|
-
hint?: string;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
type SmallVariantProps = BaseInputFileProps & {
|
|
61
|
-
variant: 'small';
|
|
62
|
-
description?: never;
|
|
63
|
-
useChangeLabel?: never;
|
|
64
|
-
changeLableProps?: never;
|
|
65
|
-
hint?: never;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
// Union type
|
|
69
|
-
type InputFileProps = DefaultVariantProps | SmallVariantProps;
|
|
17
|
+
import type {
|
|
18
|
+
FileItem,
|
|
19
|
+
InputFileProps,
|
|
20
|
+
UploadConfig,
|
|
21
|
+
UploadedFile,
|
|
22
|
+
} from './type';
|
|
23
|
+
import { uploadFile } from './helpers/uploadFile';
|
|
70
24
|
|
|
71
25
|
export default function InputFile({
|
|
72
26
|
title = 'Upload File',
|
|
73
27
|
description = 'File harus berformat JPG, PNG dan PDF',
|
|
74
|
-
accept = [
|
|
28
|
+
accept = [
|
|
29
|
+
'application/pdf',
|
|
30
|
+
'image/jpeg',
|
|
31
|
+
'image/png',
|
|
32
|
+
types.pdf,
|
|
33
|
+
types.images,
|
|
34
|
+
],
|
|
75
35
|
btnChooseFileText = 'Choose File',
|
|
76
36
|
multiple = false,
|
|
77
37
|
value,
|
|
@@ -83,39 +43,81 @@ export default function InputFile({
|
|
|
83
43
|
hint,
|
|
84
44
|
hasError,
|
|
85
45
|
onChange,
|
|
46
|
+
uploadConfig,
|
|
47
|
+
maxSize = 5, //5MB
|
|
48
|
+
maxSizeErrorMessage,
|
|
86
49
|
}: InputFileProps) {
|
|
87
|
-
const [isOpenBottomSheetTypeFile, setIsOpenBottomSheetTypeFile] =
|
|
88
|
-
|
|
89
|
-
const [
|
|
90
|
-
|
|
91
|
-
const [
|
|
92
|
-
const [
|
|
93
|
-
|
|
94
|
-
);
|
|
50
|
+
const [isOpenBottomSheetTypeFile, setIsOpenBottomSheetTypeFile] = useState(false); //prettier-ignore
|
|
51
|
+
const [isOpenBottomSheetDeleteFile, setIsOpenBottomSheetDeleteFile] = useState(false); //prettier-ignore
|
|
52
|
+
const [selectedFileIndex, setSelectedFileIndex] = useState<number | null>(null); //prettier-ignore
|
|
53
|
+
const [uploadedFiles, setUploadedFiles] = useState<UploadedFile<unknown>[]>([]); //prettier-ignore
|
|
54
|
+
const [internalErrorMessage, setInternalErrorMessage] = useState<string | null>(null); //prettier-ignore
|
|
55
|
+
const [files, setFiles] = useState<FileItem[]>(value || []);
|
|
56
|
+
const filesRef = useRef<FileItem[]>(files);
|
|
95
57
|
|
|
96
|
-
const
|
|
97
|
-
setIsOpenBottomSheetTypeFile(true);
|
|
98
|
-
};
|
|
58
|
+
const calculatedMaxSize = maxSize * 1024 * 1024;
|
|
99
59
|
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
if (selectedFileIndex !== null) {
|
|
103
|
-
confirmReplaceFile(selectedFileIndex);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
60
|
+
const uploadAndTrack = async (newFiles: FileItem[], config: UploadConfig) => {
|
|
61
|
+
setIsOpenBottomSheetTypeFile(false);
|
|
106
62
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
63
|
+
const withLoading = newFiles.map((f) => ({ ...f, uploading: true }));
|
|
64
|
+
setFiles((prev) => [...prev, ...withLoading]);
|
|
65
|
+
|
|
66
|
+
const results = await Promise.all(
|
|
67
|
+
newFiles.map((f) => uploadFile(f, config))
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const currentFiles = filesRef.current;
|
|
71
|
+
const updatedFiles = currentFiles.map((f) => {
|
|
72
|
+
const match = results.find(
|
|
73
|
+
(r) => r.originalName === ('name' in f ? f.name : f.fileName)
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
if (!match) return f;
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
...f,
|
|
80
|
+
uploading: false,
|
|
81
|
+
error: match.uploadedData === null,
|
|
82
|
+
uploadedData: match.uploadedData,
|
|
83
|
+
hint:
|
|
84
|
+
match.uploadedData === null
|
|
85
|
+
? (config.errorMessage ?? 'Upload gagal')
|
|
86
|
+
: f.hint,
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
setFiles(updatedFiles);
|
|
91
|
+
|
|
92
|
+
const newUploaded = [...uploadedFiles, ...results];
|
|
93
|
+
setUploadedFiles(newUploaded);
|
|
94
|
+
uploadConfig?.onUploadSuccess?.(newUploaded);
|
|
95
|
+
onChange?.(updatedFiles);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// validate max size
|
|
99
|
+
const validateMaxSize = (
|
|
100
|
+
assets: { size?: number | null; fileSize?: number | null }[]
|
|
101
|
+
): boolean => {
|
|
102
|
+
if (!maxSize) return true;
|
|
103
|
+
|
|
104
|
+
const oversized = assets.filter((f) => {
|
|
105
|
+
const size = f.size ?? f.fileSize ?? 0;
|
|
106
|
+
return size > (calculatedMaxSize ?? Infinity);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (oversized.length > 0) {
|
|
110
|
+
setInternalErrorMessage(
|
|
111
|
+
maxSizeErrorMessage ?? 'Ukuran file melebihi batas maksimal'
|
|
112
|
+
);
|
|
112
113
|
setIsOpenBottomSheetTypeFile(false);
|
|
113
|
-
|
|
114
|
-
if (error?.message?.toLowerCase().includes('user canceled')) return;
|
|
115
|
-
console.error('Failed to pick document:', error);
|
|
114
|
+
return false;
|
|
116
115
|
}
|
|
116
|
+
|
|
117
|
+
return true;
|
|
117
118
|
};
|
|
118
119
|
|
|
120
|
+
// preview file
|
|
119
121
|
const handlePreviewFile = async (file: any) => {
|
|
120
122
|
try {
|
|
121
123
|
let fileUri = file.uri;
|
|
@@ -145,18 +147,63 @@ export default function InputFile({
|
|
|
145
147
|
}
|
|
146
148
|
};
|
|
147
149
|
|
|
150
|
+
// pick file
|
|
151
|
+
const handlePickFile = async () => {
|
|
152
|
+
try {
|
|
153
|
+
if (selectedFileIndex !== null) {
|
|
154
|
+
confirmReplaceFile(selectedFileIndex);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const result = await pick({
|
|
159
|
+
type: accept,
|
|
160
|
+
allowMultiSelection: multiple,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const pickedFiles = multiple ? result : [result[0]];
|
|
164
|
+
|
|
165
|
+
if (!validateMaxSize(pickedFiles)) return;
|
|
166
|
+
setInternalErrorMessage(null);
|
|
167
|
+
|
|
168
|
+
if (uploadConfig) {
|
|
169
|
+
await uploadAndTrack(
|
|
170
|
+
pickedFiles as unknown as FileItem[],
|
|
171
|
+
uploadConfig
|
|
172
|
+
);
|
|
173
|
+
} else {
|
|
174
|
+
const newFiles = [...files, ...(pickedFiles as unknown as FileItem[])];
|
|
175
|
+
setFiles(newFiles);
|
|
176
|
+
onChange?.(newFiles);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
setIsOpenBottomSheetTypeFile(false);
|
|
180
|
+
} catch (error: any) {
|
|
181
|
+
if (error?.message?.toLowerCase().includes('user canceled')) return;
|
|
182
|
+
console.error('Failed to pick document:', error);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
148
186
|
const handleOnPressButtonCamera = async () => {
|
|
149
187
|
try {
|
|
150
188
|
if (selectedFileIndex !== null) {
|
|
151
|
-
confirmReplaceFile(selectedFileIndex, 'camera');
|
|
189
|
+
await confirmReplaceFile(selectedFileIndex, 'camera');
|
|
152
190
|
return;
|
|
153
191
|
}
|
|
154
192
|
|
|
155
193
|
const result = await launchCamera({ mediaType: 'photo' });
|
|
156
194
|
if (result.didCancel || !result.assets) return;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
195
|
+
|
|
196
|
+
if (!validateMaxSize(result.assets)) return;
|
|
197
|
+
setInternalErrorMessage(null);
|
|
198
|
+
|
|
199
|
+
if (uploadConfig) {
|
|
200
|
+
await uploadAndTrack(result.assets as FileItem[], uploadConfig);
|
|
201
|
+
} else {
|
|
202
|
+
const newFiles = [...files, result.assets[0] as FileItem];
|
|
203
|
+
setFiles(newFiles);
|
|
204
|
+
onChange?.(newFiles);
|
|
205
|
+
}
|
|
206
|
+
|
|
160
207
|
setIsOpenBottomSheetTypeFile(false);
|
|
161
208
|
} catch (error: any) {
|
|
162
209
|
console.error('Failed to open camera:', error);
|
|
@@ -166,7 +213,7 @@ export default function InputFile({
|
|
|
166
213
|
const handleOnPressButtonGallery = async () => {
|
|
167
214
|
try {
|
|
168
215
|
if (selectedFileIndex !== null) {
|
|
169
|
-
confirmReplaceFile(selectedFileIndex, 'gallery');
|
|
216
|
+
await confirmReplaceFile(selectedFileIndex, 'gallery');
|
|
170
217
|
return;
|
|
171
218
|
}
|
|
172
219
|
|
|
@@ -175,9 +222,18 @@ export default function InputFile({
|
|
|
175
222
|
selectionLimit: multiple ? 0 : 1,
|
|
176
223
|
});
|
|
177
224
|
if (result.didCancel || !result.assets) return;
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
225
|
+
|
|
226
|
+
if (!validateMaxSize(result.assets)) return;
|
|
227
|
+
setInternalErrorMessage(null);
|
|
228
|
+
|
|
229
|
+
if (uploadConfig) {
|
|
230
|
+
await uploadAndTrack(result.assets as FileItem[], uploadConfig);
|
|
231
|
+
} else {
|
|
232
|
+
const newFiles = [...files, ...(result.assets as FileItem[])];
|
|
233
|
+
setFiles(newFiles);
|
|
234
|
+
onChange?.(newFiles);
|
|
235
|
+
}
|
|
236
|
+
|
|
181
237
|
setIsOpenBottomSheetTypeFile(false);
|
|
182
238
|
} catch (error: any) {
|
|
183
239
|
console.error('Failed to open gallery:', error);
|
|
@@ -192,13 +248,16 @@ export default function InputFile({
|
|
|
192
248
|
const deleteFile = () => {
|
|
193
249
|
if (selectedFileIndex !== null) {
|
|
194
250
|
const newFiles = files.filter((_, i) => i !== selectedFileIndex);
|
|
251
|
+
|
|
195
252
|
setFiles(newFiles);
|
|
253
|
+
setUploadedFiles(newFiles as unknown as UploadedFile<unknown>[]);
|
|
196
254
|
onChange?.(newFiles);
|
|
197
255
|
setSelectedFileIndex(null);
|
|
198
256
|
}
|
|
199
257
|
setIsOpenBottomSheetDeleteFile(false);
|
|
200
258
|
};
|
|
201
259
|
|
|
260
|
+
// replace attachment
|
|
202
261
|
const handleReplaceFile = (index: number) => {
|
|
203
262
|
setIsOpenBottomSheetTypeFile(true);
|
|
204
263
|
setSelectedFileIndex(index);
|
|
@@ -208,33 +267,91 @@ export default function InputFile({
|
|
|
208
267
|
index: number,
|
|
209
268
|
type?: 'gallery' | 'camera'
|
|
210
269
|
) => {
|
|
211
|
-
|
|
270
|
+
const labelFile = files?.[index]?.labelFile;
|
|
271
|
+
|
|
272
|
+
// handler untuk pick
|
|
273
|
+
const pickFile = async (): Promise<FileItem | null> => {
|
|
274
|
+
if (type === 'gallery') {
|
|
275
|
+
const result = await launchImageLibrary({
|
|
276
|
+
mediaType: 'photo',
|
|
277
|
+
selectionLimit: multiple ? 0 : 1,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (!result.assets?.length) return null;
|
|
281
|
+
return { ...result.assets[0], labelFile };
|
|
282
|
+
}
|
|
212
283
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
284
|
+
if (type === 'camera') {
|
|
285
|
+
const result = await launchCamera({ mediaType: 'photo' });
|
|
286
|
+
|
|
287
|
+
if (!result.assets?.length) return null;
|
|
288
|
+
return { ...result.assets[0], labelFile: files?.[index]?.labelFile };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const result = await pick({
|
|
292
|
+
type: accept,
|
|
293
|
+
allowMultiSelection: multiple,
|
|
217
294
|
});
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
295
|
+
|
|
296
|
+
if (!result?.length) return null;
|
|
297
|
+
return { ...result[0], labelFile } as unknown as FileItem;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const replacedFile = await pickFile();
|
|
301
|
+
if (!replacedFile) return;
|
|
302
|
+
|
|
303
|
+
// handle validasi max size
|
|
304
|
+
if (!validateMaxSize([replacedFile])) return;
|
|
305
|
+
setInternalErrorMessage(null);
|
|
306
|
+
|
|
307
|
+
// handle upload
|
|
308
|
+
if (uploadConfig) {
|
|
309
|
+
setIsOpenBottomSheetTypeFile(false);
|
|
310
|
+
|
|
311
|
+
const withLoading = files.map((f, i) =>
|
|
312
|
+
i === index ? { ...replacedFile, uploading: true } : f
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
setFiles(withLoading);
|
|
316
|
+
|
|
317
|
+
const results = await Promise.all([
|
|
318
|
+
uploadFile(replacedFile, uploadConfig),
|
|
319
|
+
]);
|
|
320
|
+
|
|
321
|
+
const uploadResult = results[0];
|
|
322
|
+
const updatedFiles = filesRef.current.map((f, i) =>
|
|
323
|
+
i === index
|
|
324
|
+
? {
|
|
325
|
+
...replacedFile,
|
|
326
|
+
uploading: false,
|
|
327
|
+
uploadedData: uploadResult.uploadedData,
|
|
328
|
+
error: uploadResult.uploadedData === null,
|
|
329
|
+
hint:
|
|
330
|
+
uploadResult.uploadedData === null
|
|
331
|
+
? (uploadConfig.errorMessage ?? 'Upload gagal')
|
|
332
|
+
: replacedFile.hint,
|
|
333
|
+
}
|
|
334
|
+
: f
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
setFiles(updatedFiles);
|
|
338
|
+
onChange?.(updatedFiles);
|
|
224
339
|
} else {
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
340
|
+
const updatedFiles = files.map((f, i) =>
|
|
341
|
+
i === index ? replacedFile : f
|
|
342
|
+
);
|
|
343
|
+
setFiles(updatedFiles);
|
|
344
|
+
onChange?.(updatedFiles);
|
|
228
345
|
}
|
|
229
346
|
|
|
230
|
-
const updatedFiles = files.map((f, i) => (i === index ? replacedFile : f));
|
|
231
|
-
|
|
232
|
-
setFiles(updatedFiles);
|
|
233
|
-
onChange?.(updatedFiles);
|
|
234
347
|
setSelectedFileIndex(null);
|
|
235
348
|
setIsOpenBottomSheetTypeFile(false);
|
|
236
349
|
};
|
|
237
350
|
|
|
351
|
+
useEffect(() => {
|
|
352
|
+
filesRef.current = files;
|
|
353
|
+
}, [files]);
|
|
354
|
+
|
|
238
355
|
useEffect(() => {
|
|
239
356
|
setFiles(value ?? []);
|
|
240
357
|
}, [value]);
|
|
@@ -244,11 +361,11 @@ export default function InputFile({
|
|
|
244
361
|
{variant === 'default' && (
|
|
245
362
|
<CardTrigger
|
|
246
363
|
title={title}
|
|
247
|
-
hint={hint}
|
|
248
|
-
hasError={hasError}
|
|
364
|
+
hint={hint || (internalErrorMessage as string)}
|
|
365
|
+
hasError={hasError || !!internalErrorMessage}
|
|
249
366
|
description={description}
|
|
250
367
|
btnSelect={btnChooseFileText}
|
|
251
|
-
onPress={
|
|
368
|
+
onPress={() => setIsOpenBottomSheetTypeFile(true)}
|
|
252
369
|
/>
|
|
253
370
|
)}
|
|
254
371
|
|
|
@@ -259,11 +376,17 @@ export default function InputFile({
|
|
|
259
376
|
title={title}
|
|
260
377
|
hasError={hasError}
|
|
261
378
|
textButton={btnChooseFileText}
|
|
262
|
-
onChooseFile={
|
|
379
|
+
onChooseFile={() => setIsOpenBottomSheetTypeFile(true)}
|
|
263
380
|
onPreview={handlePreviewFile}
|
|
264
381
|
onReplace={handleReplaceFile}
|
|
265
382
|
onDelete={confirmDeleteFile}
|
|
266
383
|
/>
|
|
384
|
+
{internalErrorMessage && (
|
|
385
|
+
<Typography variant="t3" color={Color.danger[500]}>
|
|
386
|
+
{internalErrorMessage}
|
|
387
|
+
</Typography>
|
|
388
|
+
)}
|
|
389
|
+
|
|
267
390
|
{files.length > 0 && (
|
|
268
391
|
<>
|
|
269
392
|
{files
|
|
@@ -306,6 +429,7 @@ export default function InputFile({
|
|
|
306
429
|
<ItemPreview
|
|
307
430
|
index={index}
|
|
308
431
|
file={file}
|
|
432
|
+
loading={file.uploading}
|
|
309
433
|
onPress={() => handlePreviewFile(file)}
|
|
310
434
|
onReplace={() => handleReplaceFile(index)}
|
|
311
435
|
onDelete={() => confirmDeleteFile(index)}
|