Create PageWrapper component that routes to: - iOS: NewWrapper (stable, tested) - Android: NewWrapper_V2 (keyboard handling fix) New Files: - components/_ShareComponent/PageWrapper.tsx - docs/PAGEWRAPPER-USAGE.md Migrated Job Screens (10): - screens/Job/ScreenJobCreate.tsx: NewWrapper_V2 → PageWrapper - screens/Job/ScreenJobEdit.tsx: NewWrapper_V2 → PageWrapper - screens/Job/ScreenArchive.tsx: ViewWrapper → PageWrapper - screens/Job/ScreenArchive2.tsx: NewWrapper_V2 → PageWrapper - screens/Job/ScreenBeranda2.tsx: NewWrapper_V2 → PageWrapper - screens/Job/MainViewStatus2.tsx: NewWrapper_V2 → PageWrapper - app/(application)/(user)/job/[id]/index.tsx: ViewWrapper → PageWrapper - app/(application)/(user)/job/[id]/archive.tsx: ViewWrapper → PageWrapper - app/(application)/(user)/job/[id]/[status]/detail.tsx: NewWrapper_V2 → PageWrapper Benefits: - iOS users (70%+) get stable NewWrapper - Android users get keyboard handling fix - Clean API - no Platform.OS checks in screens - Easy future migration path Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
7.2 KiB
7.2 KiB
PageWrapper - Platform-Specific Wrapper
📋 Overview
PageWrapper adalah wrapper component yang secara otomatis memilih wrapper yang tepat berdasarkan platform:
- iOS: Menggunakan
NewWrapper(stable, tested) - Android: Menggunakan
NewWrapper_V2(dengan keyboard handling fix)
🎯 Kapan Menggunakan PageWrapper?
✅ Gunakan PageWrapper untuk:
- Screen baru yang kamu buat
- Migrasi screen existing dari
NewWrapper/ViewWrapper - Form screens dengan TextInput/TextArea
- List screens dengan pagination
❌ Jangan gunakan PageWrapper untuk:
- Screen yang sudah menggunakan
NewWrapper_V2langsung dan sudah tested di iOS - Custom wrapper requirements
📝 Usage
Basic Usage (Static Content)
import { PageWrapper } from "@/components";
export function MyScreen() {
return (
<PageWrapper
footerComponent={<ButtonFooter />}
>
<StackCustom>
<TextInputCustom label="Name" />
<TextAreaCustom label="Description" />
</StackCustom>
</PageWrapper>
);
}
With Keyboard Handling (Android Only)
<PageWrapper
enableKeyboardHandling
keyboardScrollOffset={100}
contentPaddingHorizontal={16}
footerComponent={<ButtonFooter />}
>
<StackCustom>
<View onStartShouldSetResponder={() => true}>
<TextInputCustom label="Name" />
</View>
<View onStartShouldSetResponder={() => true}>
<TextAreaCustom label="Description" />
</View>
</StackCustom>
</PageWrapper>
List Mode (Pagination)
<PageWrapper
listData={pagination.listData}
renderItem={renderItem}
onEndReached={pagination.loadMore}
ListEmptyComponent={ListEmptyComponent}
ListFooterComponent={ListFooterComponent}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
/>
🔧 Props
Common Props (iOS & Android)
| Prop | Type | Default | Description |
|---|---|---|---|
footerComponent |
ReactNode |
- | Fixed footer component |
headerComponent |
ReactNode |
- | Header component (sticky) |
floatingButton |
ReactNode |
- | Floating button overlay |
hideFooter |
boolean |
false |
Hide footer footer |
withBackground |
boolean |
false |
Use background image |
style |
ViewStyle |
- | Custom container style |
refreshControl |
RefreshControl |
- | Pull-to-refresh control |
Android-Only Props (Ignored di iOS)
| Prop | Type | Default | Description |
|---|---|---|---|
enableKeyboardHandling |
boolean |
false |
Enable keyboard auto-scroll |
keyboardScrollOffset |
number |
100 |
Scroll offset when keyboard appears |
contentPaddingBottom |
number |
80 |
Bottom padding for content |
contentPaddingTop |
number |
8 |
Top padding for content |
contentPaddingHorizontal |
number |
0 |
Horizontal padding for content |
📊 Platform Behavior
| Feature | iOS (NewWrapper) | Android (NewWrapper_V2) |
|---|---|---|
| Keyboard Handling | ❌ No auto-scroll | ✅ Auto-scroll to input |
| Footer Position | ✅ Fixed bottom | ✅ Fixed bottom (absolute) |
| Safe Area | ✅ Handled | ✅ Handled |
| Content Padding | Default | Customizable |
| List Mode | ✅ Supported | ✅ Supported |
🔄 Migration Guide
From NewWrapper
// BEFORE
import { NewWrapper } from "@/components";
<NewWrapper footerComponent={footer}>
{children}
</NewWrapper>
// AFTER
import { PageWrapper } from "@/components";
<PageWrapper footerComponent={footer}>
{children}
</PageWrapper>
From NewWrapper_V2
// BEFORE
import { NewWrapper_V2 } from "@/components";
<NewWrapper_V2
enableKeyboardHandling
keyboardScrollOffset={100}
footerComponent={footer}
>
<View onStartShouldSetResponder={() => true}>
<TextInputCustom />
</View>
</NewWrapper_V2>
// AFTER
import { PageWrapper } from "@/components";
<PageWrapper
enableKeyboardHandling
keyboardScrollOffset={100}
footerComponent={footer}
>
<View onStartShouldSetResponder={() => true}>
<TextInputCustom />
</View>
</PageWrapper>
⚠️ Important Notes
For Form Screens (Android)
Jika menggunakan enableKeyboardHandling, WAJIB wrap semua input dengan View onStartShouldSetResponder:
<View onStartShouldSetResponder={() => true}>
<TextInputCustom label="Name" />
</View>
Kenapa?
- Mencegah keyboard handling conflict
- Memastikan tap outside dismiss keyboard
- Konsisten behavior di Android
For iOS Users
Props berikut diabaikan di iOS:
enableKeyboardHandlingkeyboardScrollOffsetcontentPaddingBottomcontentPaddingTopcontentPaddingHorizontal
iOS menggunakan NewWrapper yang sudah stable tanpa keyboard handling.
🎨 Examples
Example 1: Simple Form
import { PageWrapper, TextInputCustom, StackCustom } from "@/components";
export function SimpleForm() {
return (
<PageWrapper
enableKeyboardHandling
keyboardScrollOffset={100}
footerComponent={<SubmitButton />}
>
<StackCustom>
<View onStartShouldSetResponder={() => true}>
<TextInputCustom label="Name" />
</View>
<View onStartShouldSetResponder={() => true}>
<TextInputCustom label="Email" />
</View>
</StackCustom>
</PageWrapper>
);
}
Example 2: List with Pagination
import { PageWrapper } from "@/components";
export function UserList() {
const pagination = usePagination({ fetchFunction: fetchUsers });
return (
<PageWrapper
listData={pagination.listData}
renderItem={({ item }) => <UserCard item={item} />}
onEndReached={pagination.loadMore}
ListEmptyComponent={<EmptyState />}
ListFooterComponent={<LoadingFooter />}
refreshControl={
<RefreshControl
refreshing={pagination.refreshing}
onRefresh={pagination.refresh}
/>
}
/>
);
}
Example 3: Detail Screen (No Footer)
import { PageWrapper } from "@/components";
export function DetailScreen() {
return (
<PageWrapper hideFooter>
<StackCustom>
<TextCustom>Title</TextCustom>
<TextCustom>Description</TextCustom>
</StackCustom>
</PageWrapper>
);
}
🚀 Future Plans
Phase 1: Current (Now)
- ✅
PageWrappercreated - ✅ iOS →
NewWrapper(stable) - ✅ Android →
NewWrapper_V2(keyboard fix)
Phase 2: iOS Migration (1-2 months)
- Fix iOS bugs di
NewWrapper_V2 - Test
NewWrapper_V2di iOS devices - Update
PageWrapperuntuk use V2 untuk iOS juga
Phase 3: Unify (3 months)
- Deprecate
NewWrapperlama - Rename
NewWrapper_V2→NewWrapper - Update
PageWrapperuntuk always use V2
📚 Related Files
components/_ShareComponent/PageWrapper.tsx- Main componentcomponents/_ShareComponent/NewWrapper.tsx- iOS wrappercomponents/_ShareComponent/NewWrapper_V2.tsx- Android wrapperhooks/useKeyboardForm.ts- Keyboard handling hook
Last Updated: 2026-04-06
Created by: AI Assistant
Status: ✅ Ready to use