@herca/rn-kit 0.1.32 → 0.1.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/lib/module/BottomSheet/BottomSheet.js +1 -1
- package/lib/module/BottomSheet/BottomSheet.js.map +1 -1
- package/lib/module/Chip/README_CHIP_ITEM.md +73 -0
- package/lib/module/Icon/Icon.js +7 -1
- package/lib/module/Icon/Icon.js.map +1 -1
- package/lib/module/Icon/README.md +4 -0
- package/lib/module/Icon/icons/DiscountFill.js +23 -0
- package/lib/module/Icon/icons/DiscountFill.js.map +1 -0
- package/lib/module/Icon/icons/LocationPinFill.js +21 -0
- package/lib/module/Icon/icons/LocationPinFill.js.map +1 -0
- package/lib/module/Icon/icons/RotateSquareFill.js +21 -0
- package/lib/module/Icon/icons/RotateSquareFill.js.map +1 -0
- package/lib/module/Select/README.md +26 -17
- package/lib/module/Select/Select.js +8 -2
- package/lib/module/Select/Select.js.map +1 -1
- package/lib/module/Skeleton/README.md +79 -0
- package/lib/module/Skeleton/Skeleton.js +73 -0
- package/lib/module/Skeleton/Skeleton.js.map +1 -0
- package/lib/module/Skeleton/type.js +4 -0
- package/lib/module/Skeleton/type.js.map +1 -0
- package/lib/module/Tab/README.md +49 -6
- package/lib/module/Tab/Tab.js +3 -3
- package/lib/module/Tab/Tab.js.map +1 -1
- package/lib/module/Tab/TabItem.js.map +1 -1
- package/lib/module/Timeline/README.md +18 -19
- package/lib/module/Timeline/TimelineItem.js +10 -8
- package/lib/module/Timeline/TimelineItem.js.map +1 -1
- package/lib/module/index.js +3 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/lib/typescript/src/Icon/Icon.d.ts.map +1 -1
- package/lib/typescript/src/Icon/icons/DiscountFill.d.ts +8 -0
- package/lib/typescript/src/Icon/icons/DiscountFill.d.ts.map +1 -0
- package/lib/typescript/src/Icon/icons/LocationPinFill.d.ts +8 -0
- package/lib/typescript/src/Icon/icons/LocationPinFill.d.ts.map +1 -0
- package/lib/typescript/src/Icon/icons/RotateSquareFill.d.ts +8 -0
- package/lib/typescript/src/Icon/icons/RotateSquareFill.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/Select/Select.d.ts +1 -1
- package/lib/typescript/src/Select/Select.d.ts.map +1 -1
- package/lib/typescript/src/Select/type.d.ts +2 -0
- package/lib/typescript/src/Select/type.d.ts.map +1 -1
- package/lib/typescript/src/Skeleton/Skeleton.d.ts +5 -0
- package/lib/typescript/src/Skeleton/Skeleton.d.ts.map +1 -0
- package/lib/typescript/src/Skeleton/type.d.ts +10 -0
- package/lib/typescript/src/Skeleton/type.d.ts.map +1 -0
- package/lib/typescript/src/Tab/Tab.d.ts.map +1 -1
- package/lib/typescript/src/Tab/TabItem.d.ts.map +1 -1
- package/lib/typescript/src/Tab/type.d.ts +1 -1
- package/lib/typescript/src/Tab/type.d.ts.map +1 -1
- package/lib/typescript/src/Timeline/TimelineItem.d.ts +2 -1
- package/lib/typescript/src/Timeline/TimelineItem.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/BottomSheet/BottomSheet.tsx +3 -1
- package/src/Chip/README_CHIP_ITEM.md +73 -0
- package/src/Icon/Icon.tsx +6 -0
- package/src/Icon/README.md +4 -0
- package/src/Icon/icons/DiscountFill.tsx +26 -0
- package/src/Icon/icons/LocationPinFill.tsx +28 -0
- package/src/Icon/icons/RotateSquareFill.tsx +22 -0
- package/src/Icon/type.tsx +3 -0
- package/src/Select/README.md +26 -17
- package/src/Select/Select.tsx +8 -1
- package/src/Select/type.tsx +2 -0
- package/src/Skeleton/README.md +79 -0
- package/src/Skeleton/Skeleton.tsx +99 -0
- package/src/Skeleton/type.ts +10 -0
- package/src/Tab/README.md +49 -6
- package/src/Tab/Tab.tsx +2 -3
- package/src/Tab/TabItem.tsx +1 -1
- package/src/Tab/type.tsx +1 -1
- package/src/Timeline/README.md +18 -19
- package/src/Timeline/TimelineItem.tsx +12 -10
- package/src/index.tsx +4 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# 📦 Komponen `ChipItem` – React Native
|
|
2
|
+
|
|
3
|
+
Komponen `ChipItem` merupakan komponen presentasional dalam bentuk "chip" atau "pill" yang digunakan sebagai bagian dari list filter/select menggunakan UI gaya chip, baik single maupun multiple select.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🔧 Props
|
|
8
|
+
|
|
9
|
+
| Prop | Type | Default | Description |
|
|
10
|
+
| -------------- | ------------------------------------------------------------------------------------------------ | ------------ | ---------------------------------------------------------------------------------- |
|
|
11
|
+
| `item` | `ChipOptionProps` | **Required** | Data item yang akan ditampilkan, berisi label, value, dan optional `disabled` |
|
|
12
|
+
| `isSelected` | `(value: boolean) => boolean` | **Required** | Fungsi untuk menentukan apakah item saat ini sedang terpilih |
|
|
13
|
+
| `color` | `'default' \| 'success' \| 'danger' \| 'primary' \| 'warning' \| 'info' \| 'purple' \| 'orange'` | `'default'` | Warna tema chip berdasarkan `CHIP_COLOR_MAP` |
|
|
14
|
+
| `block` | `boolean` | `false` | Apakah chip harus mengisi lebar penuh jika ditampilkan secara vertikal |
|
|
15
|
+
| `size` | `'small' \| 'medium' \| 'large'` | `'medium'` | Ukuran padding dan font chip |
|
|
16
|
+
| `renderItem` | `(item: ChipOptionProps, selected: boolean, disabled: boolean) => ReactNode` | `undefined` | Fungsi untuk mengkustomisasi isi tampilan chip |
|
|
17
|
+
| `isHorizontal` | `boolean` | `false` | Jika true, maka `block` akan diabaikan agar chip tetap fleksibel secara horizontal |
|
|
18
|
+
| `onPress` | `(disabled: boolean, val: ChipOptionProps) => void` | `undefined` | Callback saat chip ditekan |
|
|
19
|
+
| `onLayout` | `(event: LayoutChangeEvent) => void` | `undefined` | Callback ketika layout chip dihitung oleh React Native |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 🎨 Style Behavior
|
|
24
|
+
|
|
25
|
+
- **Warna Dinamis**: Warna border, background, dan teks akan menyesuaikan status:
|
|
26
|
+
- Default
|
|
27
|
+
- Selected
|
|
28
|
+
- Disabled
|
|
29
|
+
|
|
30
|
+
- **Ukuran**:
|
|
31
|
+
- `'small'`: Padding kecil, font size 12
|
|
32
|
+
- `'medium'`: Default, font size 14
|
|
33
|
+
- `'large'`: Padding besar, font size 16
|
|
34
|
+
|
|
35
|
+
- **Block Mode**:
|
|
36
|
+
- Bila `block: true` dan `isHorizontal: false`, chip akan mengisi lebar penuh kontainer
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 💡 Contoh Penggunaan
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
<ChipItem
|
|
44
|
+
item={{ label: 'Marketing', value: 'marketing' }}
|
|
45
|
+
isSelected={() => true}
|
|
46
|
+
color="primary"
|
|
47
|
+
size="medium"
|
|
48
|
+
onPress={(disabled, val) => console.log('Pressed:', val)}
|
|
49
|
+
/>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 🛠️ Tips
|
|
55
|
+
|
|
56
|
+
- Gunakan `renderItem` untuk mengganti isi chip dengan tampilan kustom.
|
|
57
|
+
- Pastikan `isSelected` di-define dengan benar agar state visual chip sesuai.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 🧱 Dependencies
|
|
62
|
+
|
|
63
|
+
- `Typography`
|
|
64
|
+
- `CHIP_COLOR_MAP`
|
|
65
|
+
- `Pressable` & `StyleSheet` dari React Native
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 🗂️ Related
|
|
70
|
+
|
|
71
|
+
- `Chip`
|
|
72
|
+
- `Select`
|
|
73
|
+
- `ChipOptionProps`
|
package/src/Icon/Icon.tsx
CHANGED
|
@@ -60,6 +60,9 @@ import Bold from './icons/Bold';
|
|
|
60
60
|
import Italic from './icons/Italic';
|
|
61
61
|
import UnderLine from './icons/UnderLine';
|
|
62
62
|
import SettingsSlider from './icons/SettingsSlider';
|
|
63
|
+
import RotateSquareFill from './icons/RotateSquareFill';
|
|
64
|
+
import LocationPinFill from './icons/LocationPinFill';
|
|
65
|
+
import DiscountFill from './icons/DiscountFill';
|
|
63
66
|
|
|
64
67
|
const icons: Record<
|
|
65
68
|
IconNameProps,
|
|
@@ -124,6 +127,9 @@ const icons: Record<
|
|
|
124
127
|
'Italic': Italic,
|
|
125
128
|
'UnderLine': UnderLine,
|
|
126
129
|
'settings-slider': SettingsSlider,
|
|
130
|
+
'rotate-square-fill': RotateSquareFill,
|
|
131
|
+
'location-pin-fill': LocationPinFill,
|
|
132
|
+
'discount-fill': DiscountFill,
|
|
127
133
|
};
|
|
128
134
|
|
|
129
135
|
const Icon: React.FC<IconProps> = ({
|
package/src/Icon/README.md
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import Svg, { Path } from 'react-native-svg';
|
|
2
|
+
|
|
3
|
+
interface Props {
|
|
4
|
+
size?: number;
|
|
5
|
+
color?: string;
|
|
6
|
+
style?: object;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const DiscountFill: React.FC<Props> = ({ size = 24, color, style }: Props) => (
|
|
10
|
+
<Svg
|
|
11
|
+
width={size + 1}
|
|
12
|
+
height={size}
|
|
13
|
+
viewBox="0 0 24 25"
|
|
14
|
+
fill="none"
|
|
15
|
+
style={style}
|
|
16
|
+
>
|
|
17
|
+
<Path
|
|
18
|
+
fill-rule="evenodd"
|
|
19
|
+
clip-rule="evenodd"
|
|
20
|
+
d="M20.5589 11.3975L19.243 10.0815C18.962 9.80047 18.8039 9.41847 18.8039 9.02047V7.16247C18.8039 6.33147 18.1299 5.65749 17.2989 5.65749H15.441C15.043 5.65749 14.662 5.49947 14.38 5.21847L13.0639 3.90249C12.4759 3.31449 11.524 3.31449 10.936 3.90249L9.61996 5.21847C9.33895 5.49947 8.95692 5.65749 8.55892 5.65749H6.70101C5.87001 5.65749 5.196 6.33147 5.196 7.16247V9.02047C5.196 9.41847 5.03792 9.79947 4.75692 10.0815L3.441 11.3975C2.853 11.9855 2.853 12.9375 3.441 13.5255L4.75692 14.8415C5.03792 15.1225 5.196 15.5045 5.196 15.9025V17.7605C5.196 18.5915 5.87001 19.2655 6.70101 19.2655H8.55892C8.95692 19.2655 9.33795 19.4235 9.61996 19.7045L10.936 21.0205C11.524 21.6085 12.4759 21.6085 13.0639 21.0205L14.38 19.7045C14.661 19.4235 15.043 19.2655 15.441 19.2655H17.2989C18.1299 19.2655 18.8039 18.5915 18.8039 17.7605V15.9025C18.8039 15.5045 18.962 15.1235 19.243 14.8415L20.5589 13.5255C21.1469 12.9375 21.1469 11.9855 20.5589 11.3975ZM8.51497 9.96148C8.51497 9.40948 8.95797 8.96148 9.50997 8.96148H9.51998C10.073 8.96148 10.52 9.40948 10.52 9.96148C10.52 10.5135 10.073 10.9615 9.51998 10.9615C8.96798 10.9615 8.51497 10.5135 8.51497 9.96148ZM10.03 15.4915C9.88399 15.6375 9.69196 15.7115 9.49996 15.7115C9.30796 15.7115 9.11593 15.6385 8.96993 15.4915C8.67693 15.1985 8.67693 14.7235 8.96993 14.4305L13.9699 9.43047C14.2629 9.13747 14.738 9.13747 15.031 9.43047C15.324 9.72347 15.324 10.1985 15.031 10.4915L10.03 15.4915ZM14.52 15.9615C13.968 15.9615 13.515 15.5135 13.515 14.9615C13.515 14.4095 13.958 13.9615 14.51 13.9615H14.52C15.073 13.9615 15.52 14.4095 15.52 14.9615C15.52 15.5135 15.072 15.9615 14.52 15.9615Z"
|
|
21
|
+
fill={color}
|
|
22
|
+
/>
|
|
23
|
+
</Svg>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export default DiscountFill;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import Svg, { Path } from 'react-native-svg';
|
|
2
|
+
|
|
3
|
+
interface Props {
|
|
4
|
+
size?: number;
|
|
5
|
+
color?: string;
|
|
6
|
+
style?: object;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const LocationPinFill: React.FC<Props> = ({
|
|
10
|
+
size = 24,
|
|
11
|
+
color,
|
|
12
|
+
style,
|
|
13
|
+
}: Props) => (
|
|
14
|
+
<Svg
|
|
15
|
+
width={size}
|
|
16
|
+
height={size + 1}
|
|
17
|
+
viewBox="0 0 24 25"
|
|
18
|
+
fill="none"
|
|
19
|
+
style={style}
|
|
20
|
+
>
|
|
21
|
+
<Path
|
|
22
|
+
d="M12 2.96149C7.313 2.96149 3.5 6.77449 3.5 11.4615C3.5 16.4445 8.129 19.5025 11.192 21.5255L11.722 21.8775C11.806 21.9335 11.903 21.9615 11.999 21.9615C12.095 21.9615 12.192 21.9335 12.276 21.8775L12.806 21.5255C15.869 19.5025 20.498 16.4445 20.498 11.4615C20.5 6.77449 16.687 2.96149 12 2.96149ZM12 13.9615C10.619 13.9615 9.5 12.8425 9.5 11.4615C9.5 10.0805 10.619 8.96149 12 8.96149C13.381 8.96149 14.5 10.0805 14.5 11.4615C14.5 12.8425 13.381 13.9615 12 13.9615Z"
|
|
23
|
+
fill={color}
|
|
24
|
+
/>
|
|
25
|
+
</Svg>
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export default LocationPinFill;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import Svg, { Path } from 'react-native-svg';
|
|
2
|
+
|
|
3
|
+
interface Props {
|
|
4
|
+
size?: number;
|
|
5
|
+
color?: string;
|
|
6
|
+
style?: object;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const RotateSquareFill: React.FC<Props> = ({
|
|
10
|
+
size = 24,
|
|
11
|
+
color,
|
|
12
|
+
style,
|
|
13
|
+
}: Props) => (
|
|
14
|
+
<Svg width={size} height={size} viewBox="0 0 20 20" fill="none" style={style}>
|
|
15
|
+
<Path
|
|
16
|
+
d="M14.6875 2.5H5.3125C3.4375 2.5 2.5 3.4375 2.5 5.3125V14.6875C2.5 16.5625 3.4375 17.5 5.3125 17.5H14.6875C16.5625 17.5 17.5 16.5625 17.5 14.6875V5.3125C17.5 3.4375 16.5625 2.5 14.6875 2.5ZM14.2251 11.0891C13.7409 13.0232 12.0033 14.375 10 14.375C8.79667 14.375 7.68167 13.8733 6.875 13.0467V13.3333C6.875 13.6783 6.595 13.9583 6.25 13.9583C5.905 13.9583 5.625 13.6783 5.625 13.3333V11.25C5.625 10.905 5.905 10.625 6.25 10.625H6.4858C6.49413 10.625 6.5033 10.6242 6.51164 10.625H8.33333C8.67833 10.625 8.95833 10.905 8.95833 11.25C8.95833 11.595 8.67833 11.875 8.33333 11.875H7.50407C8.08407 12.6467 9.00167 13.125 10 13.125C11.4292 13.125 12.6675 12.1634 13.0125 10.7859C13.0967 10.4509 13.4424 10.25 13.7699 10.3316C14.1058 10.415 14.3092 10.7541 14.2251 11.0891ZM14.375 8.75C14.375 9.095 14.095 9.375 13.75 9.375H13.5067C13.5017 9.375 13.4966 9.375 13.4908 9.375H11.6667C11.3217 9.375 11.0417 9.095 11.0417 8.75C11.0417 8.405 11.3217 8.125 11.6667 8.125H12.4959C11.9159 7.35333 10.9983 6.875 10 6.875C8.57083 6.875 7.33251 7.83657 6.98751 9.21407C6.91667 9.49824 6.66163 9.6875 6.38163 9.6875C6.3308 9.6875 6.28008 9.68169 6.22925 9.66919C5.89425 9.58502 5.6908 9.24593 5.77413 8.91093C6.2583 6.97676 7.99585 5.625 9.99919 5.625C11.2025 5.625 12.3175 6.12666 13.1242 6.95333V6.66667C13.1242 6.32167 13.4042 6.04167 13.7492 6.04167C14.0942 6.04167 14.3742 6.32167 14.3742 6.66667V8.75H14.375Z"
|
|
17
|
+
fill={color}
|
|
18
|
+
/>
|
|
19
|
+
</Svg>
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export default RotateSquareFill;
|
package/src/Icon/type.tsx
CHANGED
package/src/Select/README.md
CHANGED
|
@@ -14,22 +14,23 @@ Komponen `Select` adalah komponen pemilihan data berbasis bottom sheet dengan du
|
|
|
14
14
|
|
|
15
15
|
## 📦 Props
|
|
16
16
|
|
|
17
|
-
| Prop
|
|
18
|
-
|
|
19
|
-
| `isOpen`
|
|
20
|
-
| `data`
|
|
21
|
-
| `multiple`
|
|
22
|
-
| `renderItem`
|
|
23
|
-
| `onSubmit`
|
|
24
|
-
| `onClose`
|
|
25
|
-
| `onSearch`
|
|
26
|
-
| `required`
|
|
27
|
-
| `loading`
|
|
28
|
-
| `delaySearch
|
|
29
|
-
| `height`
|
|
30
|
-
| `onRefresh`
|
|
31
|
-
| `header`
|
|
32
|
-
| `footer`
|
|
17
|
+
| Prop | Tipe | Default | Keterangan |
|
|
18
|
+
| ------------- | ----------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
19
|
+
| `isOpen` | `boolean` | `false` | Menentukan apakah bottom sheet terbuka |
|
|
20
|
+
| `data` | `ChipOptionProps[]` | - | Data pilihan untuk ditampilkan |
|
|
21
|
+
| `multiple` | `boolean` | `false` | Mengizinkan multiple selection |
|
|
22
|
+
| `renderItem` | `(item, selected, disabled) => React.ReactNode` | - | Kustom tampilan item |
|
|
23
|
+
| `onSubmit` | `(selected: ChipSelectedProps) => void` | - | Callback saat tombol submit ditekan |
|
|
24
|
+
| `onClose` | `(val: boolean) => void` | - | Callback saat bottom sheet ditutup |
|
|
25
|
+
| `onSearch` | `(val: string) => void` | - | Callback saat input pencarian berubah |
|
|
26
|
+
| `required` | `boolean` | `false` | Jika `true`, validasi input saat submit |
|
|
27
|
+
| `loading` | `boolean` | `false` | Menampilkan loading saat data di-fetch |
|
|
28
|
+
| `delaySearch` | `number` | `300` | Delay pencarian dalam milidetik |
|
|
29
|
+
| `height` | `BottomSheetHeighProps` | - | Ukuran tinggi BottomSheet |
|
|
30
|
+
| `onRefresh` | `() => void` | - | Callback saat pull-to-refresh dilakukan |
|
|
31
|
+
| `header` | ReactElement | – | Jika ingin chip mempunyai header. |
|
|
32
|
+
| `footer` | ReactElement | – | Jika ingin chip mempunyai footer. |
|
|
33
|
+
| `value` | ChipSelectedProps | – | Nilai pilihan yang dikontrol dari luar. Cocok untuk membuat komponen controlled. Akan mensinkronkan state internal selected dengan value setiap kali berubah. |
|
|
33
34
|
|
|
34
35
|
## 📌 Contoh Penggunaan
|
|
35
36
|
|
|
@@ -53,6 +54,7 @@ const MyScreen = () => {
|
|
|
53
54
|
<Button title="Pilih Data" onPress={() => setOpen(true)} />
|
|
54
55
|
<Select
|
|
55
56
|
isOpen={open}
|
|
57
|
+
value={selected}
|
|
56
58
|
data={data}
|
|
57
59
|
multiple={true}
|
|
58
60
|
onClose={() => setOpen(false)}
|
|
@@ -77,4 +79,11 @@ const MyScreen = () => {
|
|
|
77
79
|
- `Loading`
|
|
78
80
|
- `Toast` (untuk validasi/error)
|
|
79
81
|
|
|
80
|
-
- Jika ingin menambahkan animasi shimmer saat loading, bisa modifikasi komponen `Loading`.
|
|
82
|
+
- Jika ingin menambahkan animasi shimmer saat loading, bisa modifikasi komponen `Loading`.
|
|
83
|
+
|
|
84
|
+
## ⚠️ Behavior Penting
|
|
85
|
+
|
|
86
|
+
- Jika `multiple={false}` dan `required={true}`, maka:
|
|
87
|
+
- Item yang dipilih akan langsung dikirim ke `onSubmit`.
|
|
88
|
+
- BottomSheet akan langsung tertutup tanpa menekan tombol submit.
|
|
89
|
+
- Jika `required={false}`, user bisa membatalkan pilihan (deselect), dan mengirim data kosong.
|
package/src/Select/Select.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useState } from 'react';
|
|
1
|
+
import { useCallback, useState, useEffect } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Keyboard,
|
|
4
4
|
StyleSheet,
|
|
@@ -32,6 +32,7 @@ export default function Select({
|
|
|
32
32
|
header,
|
|
33
33
|
onEndReached,
|
|
34
34
|
submitBtnLabel,
|
|
35
|
+
value,
|
|
35
36
|
}: SelectProps) {
|
|
36
37
|
// const [isOpenSelect, setIsOpenSelect] = useState<boolean | undefined>(false);
|
|
37
38
|
const [selected, setSelected] = useState<ChipSelectedProps>();
|
|
@@ -75,6 +76,12 @@ export default function Select({
|
|
|
75
76
|
}, delaySearch);
|
|
76
77
|
};
|
|
77
78
|
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (value !== undefined) {
|
|
81
|
+
setSelected(value);
|
|
82
|
+
}
|
|
83
|
+
}, [value]);
|
|
84
|
+
|
|
78
85
|
return (
|
|
79
86
|
<BottomSheet
|
|
80
87
|
onClose={(val: boolean) => handleOnCloseBottom(val)}
|
package/src/Select/type.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { ChipOnSelectProps, ChipOptionProps } from '../Chip/type';
|
|
3
3
|
import type { BottomSheetHeighProps } from '../BottomSheet/type';
|
|
4
|
+
import type { ChipSelectedProps } from '../Chip/type';
|
|
4
5
|
|
|
5
6
|
export interface SelectDataProps {
|
|
6
7
|
value: string | number;
|
|
@@ -29,6 +30,7 @@ export interface SelectProps {
|
|
|
29
30
|
onEndReached?: () => void;
|
|
30
31
|
refreshing?: boolean;
|
|
31
32
|
submitBtnLabel?: string;
|
|
33
|
+
value?: ChipSelectedProps;
|
|
32
34
|
footer?: React.ComponentType<any> | React.ReactElement | null | undefined;
|
|
33
35
|
header?: React.ComponentType<any> | React.ReactElement | null | undefined;
|
|
34
36
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# 🦴 Skeleton Loader Component
|
|
2
|
+
|
|
3
|
+
Komponen `Skeleton` adalah placeholder UI yang menampilkan efek shimmer animasi, berguna untuk memberikan feedback loading saat data belum tersedia.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 📦 Props
|
|
8
|
+
|
|
9
|
+
| Prop | Tipe | Default | Deskripsi |
|
|
10
|
+
| ----------------- | ---------------- | ---------------------------- | ---------------------------------------------------------------------------------- |
|
|
11
|
+
| `width` | `DimensionValue` | `"100%"` | Lebar dari skeleton block. Bisa berupa angka atau string (`'100%'`, `'50%'`, dsb). |
|
|
12
|
+
| `height` | `DimensionValue` | `50` | Tinggi dari skeleton block. |
|
|
13
|
+
| `rounded` | `number` | `8` | Radius border untuk membuat sudut membulat. |
|
|
14
|
+
| `backgroundColor` | `string` | `"#F0F0F0"` | Warna latar belakang dari blok skeleton. |
|
|
15
|
+
| `shimmerColor` | `string` | `"rgba(255, 255, 255, 0.7)"` | Warna efek shimmer yang bergerak. |
|
|
16
|
+
| `duration` | `number` | `1500` | Durasi animasi shimmer (dalam milidetik). |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 🧠 Cara Kerja
|
|
21
|
+
|
|
22
|
+
- Komponen menggunakan `Animated.Value` untuk menggerakkan efek shimmer dari kiri ke kanan secara loop.
|
|
23
|
+
- Lebar shimmer dihitung berdasarkan lebar aktual komponen yang didapat dari event `onLayout`.
|
|
24
|
+
- Shimmer bergerak dengan `translateX` menggunakan `interpolate()` untuk efek linear mulus.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 📌 Contoh Penggunaan
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import React from 'react';
|
|
32
|
+
import { View } from 'react-native';
|
|
33
|
+
import Skeleton from './components/Skeleton';
|
|
34
|
+
|
|
35
|
+
const MyLoadingScreen = () => {
|
|
36
|
+
return (
|
|
37
|
+
<View style={{ padding: 16 }}>
|
|
38
|
+
<Skeleton height={20} width="60%" />
|
|
39
|
+
<View style={{ height: 16 }} />
|
|
40
|
+
<Skeleton height={20} width="80%" />
|
|
41
|
+
<View style={{ height: 16 }} />
|
|
42
|
+
<Skeleton height={100} width="100%" rounded={12} />
|
|
43
|
+
</View>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 🧱 Struktur Rendered
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<View>
|
|
54
|
+
{' '}
|
|
55
|
+
// container
|
|
56
|
+
<Animated.View /> // shimmer yang bergerak horizontal
|
|
57
|
+
</View>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## ⚠️ Catatan
|
|
63
|
+
|
|
64
|
+
- Komponen ini **tidak menerima children**, hanya untuk placeholder visual.
|
|
65
|
+
- Efek shimmer akan selalu berjalan secara loop selama komponen hidup.
|
|
66
|
+
- Perhatikan performa jika digunakan secara masif di satu halaman.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 🛠 Tips
|
|
71
|
+
|
|
72
|
+
- Gunakan `Skeleton` dalam daftar (list, table, card) untuk meningkatkan UX saat loading.
|
|
73
|
+
- Dapat digabungkan dengan kondisi seperti:
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
{
|
|
77
|
+
isLoading ? <Skeleton height={50} /> : <ActualComponent data={data} />;
|
|
78
|
+
}
|
|
79
|
+
```
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Animated,
|
|
4
|
+
Easing,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
View,
|
|
7
|
+
type LayoutChangeEvent,
|
|
8
|
+
type StyleProp,
|
|
9
|
+
type ViewStyle,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
import type { SkeletonProps } from './type';
|
|
12
|
+
|
|
13
|
+
const Skeleton: React.FC<SkeletonProps> = ({
|
|
14
|
+
width = '100%',
|
|
15
|
+
height = 50,
|
|
16
|
+
rounded = 8,
|
|
17
|
+
backgroundColor = '#F0F0F0',
|
|
18
|
+
shimmerColor = 'rgba(255, 255, 255, 0.7)',
|
|
19
|
+
duration = 1500,
|
|
20
|
+
}) => {
|
|
21
|
+
const shimmerAnimation = useRef(new Animated.Value(0)).current;
|
|
22
|
+
const skeletonWidth = useRef(0);
|
|
23
|
+
|
|
24
|
+
const handleLayout = (event: LayoutChangeEvent) => {
|
|
25
|
+
skeletonWidth.current = event.nativeEvent.layout.width;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const animateShimmer = () => {
|
|
30
|
+
Animated.loop(
|
|
31
|
+
Animated.sequence([
|
|
32
|
+
Animated.timing(shimmerAnimation, {
|
|
33
|
+
toValue: 1,
|
|
34
|
+
duration: duration,
|
|
35
|
+
easing: Easing.linear,
|
|
36
|
+
useNativeDriver: true,
|
|
37
|
+
}),
|
|
38
|
+
Animated.timing(shimmerAnimation, {
|
|
39
|
+
toValue: 0,
|
|
40
|
+
duration: 0,
|
|
41
|
+
useNativeDriver: true,
|
|
42
|
+
}),
|
|
43
|
+
])
|
|
44
|
+
).start();
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
animateShimmer();
|
|
48
|
+
|
|
49
|
+
return () => {
|
|
50
|
+
shimmerAnimation.stopAnimation();
|
|
51
|
+
};
|
|
52
|
+
}, [shimmerAnimation, duration]);
|
|
53
|
+
|
|
54
|
+
const shimmerTranslate = shimmerAnimation.interpolate({
|
|
55
|
+
inputRange: [0, 1],
|
|
56
|
+
// Gunakan lebar skeleton untuk perhitungan yang presisi
|
|
57
|
+
outputRange: [-200, skeletonWidth.current + 200],
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<View
|
|
62
|
+
onLayout={handleLayout}
|
|
63
|
+
style={[
|
|
64
|
+
styles.skeletonCard,
|
|
65
|
+
{
|
|
66
|
+
width,
|
|
67
|
+
height,
|
|
68
|
+
backgroundColor,
|
|
69
|
+
borderRadius: rounded,
|
|
70
|
+
} as StyleProp<ViewStyle>,
|
|
71
|
+
]}
|
|
72
|
+
>
|
|
73
|
+
<Animated.View
|
|
74
|
+
style={[
|
|
75
|
+
styles.shimmer,
|
|
76
|
+
{
|
|
77
|
+
backgroundColor: shimmerColor,
|
|
78
|
+
transform: [{ translateX: shimmerTranslate }],
|
|
79
|
+
borderRadius: rounded,
|
|
80
|
+
},
|
|
81
|
+
]}
|
|
82
|
+
/>
|
|
83
|
+
</View>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export default Skeleton;
|
|
88
|
+
|
|
89
|
+
const styles = StyleSheet.create({
|
|
90
|
+
skeletonCard: {
|
|
91
|
+
overflow: 'hidden',
|
|
92
|
+
},
|
|
93
|
+
shimmer: {
|
|
94
|
+
...StyleSheet.absoluteFillObject,
|
|
95
|
+
width: '90%',
|
|
96
|
+
height: '100%',
|
|
97
|
+
opacity: 0.3,
|
|
98
|
+
},
|
|
99
|
+
});
|
package/src/Tab/README.md
CHANGED
|
@@ -2,13 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
Komponen tab ringan dan fleksibel untuk React Native yang mendukung:
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
- ✅ Swipe kiri/kanan antar tab
|
|
6
|
+
- ✅ Scroll vertikal di dalam konten tiap tab
|
|
7
|
+
- ✅ Tampilan tab yang bisa disesuaikan
|
|
8
|
+
- ✅ Tanpa dependensi eksternal (tidak butuh react-navigation)
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
+
## ⚙️ Instalasi
|
|
13
|
+
|
|
14
|
+
Komponen tab ini dibangun menggunakan [react-native-pager-view](https://github.com/callstack/react-native-pager-view) untuk mengatur perpindahan tab secara swipe kiri/kanan.
|
|
15
|
+
Pastikan Anda menginstal dependensi ini sebelum menggunakan komponen.
|
|
16
|
+
|
|
17
|
+
### 1. Install Dependensi
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
# Menggunakan npm
|
|
21
|
+
npm install react-native-pager-view
|
|
22
|
+
|
|
23
|
+
# Menggunakan yarn
|
|
24
|
+
yarn add react-native-pager-view
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 2. Konfigurasi Platform
|
|
28
|
+
|
|
29
|
+
#### iOS
|
|
30
|
+
|
|
31
|
+
Setelah instalasi, jalankan perintah berikut untuk meng-install pod:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
cd ios && pod install && cd ..
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Tidak ada konfigurasi tambahan untuk iOS, namun pastikan proyek Anda sudah menggunakan **minimum iOS 11** atau sesuai rekomendasi React Native agar kompatibel.
|
|
38
|
+
|
|
39
|
+
#### Android
|
|
40
|
+
|
|
41
|
+
`react-native-pager-view` akan otomatis ter-link di Android tanpa perlu langkah tambahan.
|
|
42
|
+
Namun, Anda perlu memastikan bahwa **minimum SDK version** Anda adalah 21 atau lebih tinggi.
|
|
43
|
+
Periksa file `android/app/build.gradle`:
|
|
44
|
+
|
|
45
|
+
```gradle
|
|
46
|
+
android {
|
|
47
|
+
defaultConfig {
|
|
48
|
+
minSdkVersion = 21
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Jika nilai `minSdkVersion` di bawah 21, tingkatkan agar komponen dapat berjalan dengan baik.
|
|
54
|
+
|
|
12
55
|
## 💡 Contoh Penggunaan
|
|
13
56
|
|
|
14
57
|
```tsx
|
|
@@ -33,8 +76,8 @@ export default function App() {
|
|
|
33
76
|
|
|
34
77
|
### 1. Navigasi Tab
|
|
35
78
|
|
|
36
|
-
|
|
37
|
-
|
|
79
|
+
- Tap pada header tab untuk berpindah
|
|
80
|
+
- Swipe kiri/kanan untuk berpindah antar tab
|
|
38
81
|
|
|
39
82
|
### 2. Konten Scrollable
|
|
40
83
|
|
package/src/Tab/Tab.tsx
CHANGED
|
@@ -76,10 +76,9 @@ const Tab: React.FC<TabProps> = ({ children, current = 0, onChangeTab }) => {
|
|
|
76
76
|
onPageSelected={(e) => updateActive(e.nativeEvent.position)}
|
|
77
77
|
>
|
|
78
78
|
{items.map((item, index) => {
|
|
79
|
-
const Comp = item.props.component;
|
|
80
79
|
return (
|
|
81
80
|
<View key={index} collapsable={false} style={{ flex: 1 }}>
|
|
82
|
-
{
|
|
81
|
+
{item.props.children}
|
|
83
82
|
</View>
|
|
84
83
|
);
|
|
85
84
|
})}
|
|
@@ -108,5 +107,5 @@ const styles = StyleSheet.create({
|
|
|
108
107
|
},
|
|
109
108
|
tabActive: { backgroundColor: Color.primary[1000] },
|
|
110
109
|
tabInactive: { backgroundColor: Color.gray[100] },
|
|
111
|
-
pager: { flex: 1 },
|
|
110
|
+
pager: { flex: 1, backgroundColor: Color.base.white100 },
|
|
112
111
|
});
|
package/src/Tab/TabItem.tsx
CHANGED
package/src/Tab/type.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import React, { type ReactElement } from 'react';
|
|
|
2
2
|
|
|
3
3
|
export interface TabItemProps {
|
|
4
4
|
name: string | number;
|
|
5
|
-
|
|
5
|
+
children: React.ReactNode;
|
|
6
6
|
}
|
|
7
7
|
export interface TabProps {
|
|
8
8
|
children: ReactElement<TabItemProps> | ReactElement<TabItemProps>[];
|