feat: Complete OS_Wrapper implementation with keyboard handling and PADDING_INLINE

OS_Wrapper System:
- Simplify API: Remove PageWrapper, merge keyboard props into OS_Wrapper
- Add auto-scroll when keyboard appears (Android only)
- Add tap-to-dismiss keyboard for both Static and List modes
- Fix contentPaddingBottom default to 250px (prevent keyboard overlap)
- Change default contentPadding to 0 (per-screen control)
- Remove Platform.OS checks from IOSWrapper and AndroidWrapper

Constants:
- Add PADDING_INLINE constant (16px) for consistent inline padding
- Add OS_PADDING_TOP constants for tab layouts

Job Screens Migration (9 files):
- Apply PADDING_INLINE to all Job screens:
  - ScreenBeranda, ScreenBeranda2
  - ScreenArchive, ScreenArchive2
  - MainViewStatus, MainViewStatus2
  - ScreenJobCreate, ScreenJobEdit
  - Job detail screen

Keyboard Handling:
- Simplified useKeyboardForm hook
- Auto-scroll by keyboard height when keyboard appears
- Track scroll position for accurate scroll targets
- TouchableWithoutFeedback wraps all content for tap-to-dismiss

Documentation:
- Update TASK-005 with Phase 1 completion status
- Update Quick Reference with unified API examples

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-04-07 17:50:15 +08:00
parent 502cd7bc65
commit 1a5ca78041
20 changed files with 349 additions and 238 deletions

View File

@@ -12,6 +12,7 @@ import { apiJobGetByStatus } from "@/service/api-client/api-job";
import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { PADDING_INLINE } from "@/constants/constans-value";
export default function Job_MainViewStatus() {
const { user } = useAuth();
@@ -64,7 +65,7 @@ export default function Job_MainViewStatus() {
return (
<>
<OS_Wrapper headerComponent={scrollComponent} hideFooter>
<OS_Wrapper headerComponent={scrollComponent} hideFooter contentPadding={PADDING_INLINE}>
{isLoadList ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (

View File

@@ -1,7 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { BaseBox, OS_Wrapper, ScrollableCustom, TextCustom } from "@/components";
import { MainColor } from "@/constants/color-palet";
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
import { PAGINATION_DEFAULT_TAKE, PADDING_INLINE } from "@/constants/constans-value";
import { createPaginationComponents } from "@/helpers/paginationHelpers";
import { useAuth } from "@/hooks/use-auth";
import { usePagination } from "@/hooks/use-pagination";
@@ -102,6 +102,7 @@ export default function Job_MainViewStatus2() {
ListEmptyComponent={ListEmptyComponent}
ListFooterComponent={ListFooterComponent}
hideFooter
contentPadding={PADDING_INLINE}
/>
);
}

View File

@@ -5,6 +5,7 @@ import { apiJobGetAll } from "@/service/api-client/api-job";
import { useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { PADDING_INLINE } from "@/constants/constans-value";
export default function Job_ScreenArchive() {
const { user } = useAuth();
@@ -33,7 +34,7 @@ export default function Job_ScreenArchive() {
};
return (
<OS_Wrapper hideFooter>
<OS_Wrapper hideFooter contentPadding={PADDING_INLINE}>
{isLoadData ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (

View File

@@ -1,5 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { BaseBox, OS_Wrapper, TextCustom, ViewWrapper } from "@/components";
import { BaseBox, OS_Wrapper, TextCustom } from "@/components";
import { MainColor } from "@/constants/color-palet";
import { createPaginationComponents } from "@/helpers/paginationHelpers";
import { useAuth } from "@/hooks/use-auth";
@@ -9,7 +9,7 @@ import { useFocusEffect } from "expo-router";
import _ from "lodash";
import { useState } from "react";
import { RefreshControl } from "react-native";
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
import { PAGINATION_DEFAULT_TAKE, PADDING_INLINE } from "@/constants/constans-value";
export default function Job_ScreenArchive2() {
const { user } = useAuth();
@@ -70,6 +70,7 @@ export default function Job_ScreenArchive2() {
ListEmptyComponent={ListEmptyComponent}
ListFooterComponent={ListFooterComponent}
hideFooter
contentPadding={PADDING_INLINE}
/>
);
}

View File

@@ -13,6 +13,7 @@ import { apiJobGetAll } from "@/service/api-client/api-job";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { PADDING_INLINE } from "@/constants/constans-value";
export default function Job_ScreenBeranda() {
const [listData, setListData] = useState<any[]>([]);
@@ -45,6 +46,7 @@ export default function Job_ScreenBeranda() {
return (
<OS_Wrapper
hideFooter
contentPadding={PADDING_INLINE}
floatingButton={
<FloatingButton onPress={() => router.push("/job/create")} />
}

View File

@@ -7,7 +7,6 @@ import {
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import { createPaginationComponents } from "@/helpers/paginationHelpers";
@@ -17,7 +16,7 @@ import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useState } from "react";
import { RefreshControl, View } from "react-native";
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
import { PAGINATION_DEFAULT_TAKE, PADDING_INLINE } from "@/constants/constans-value";
const PAGE_SIZE = 10;
@@ -76,6 +75,7 @@ export default function Job_ScreenBeranda2() {
return (
<OS_Wrapper
hideFooter
contentPadding={PADDING_INLINE}
headerComponent={
<View style={{ paddingTop: 8 }}>
<SearchInput

View File

@@ -4,13 +4,14 @@ import {
ButtonCustom,
InformationBox,
LandscapeFrameUploaded,
PageWrapper,
OS_Wrapper,
Spacing,
StackCustom,
TextAreaCustom,
TextInputCustom,
} from "@/components";
import DIRECTORY_ID from "@/constants/directory-id";
import { PADDING_INLINE } from "@/constants/constans-value";
import { useAuth } from "@/hooks/use-auth";
import { apiJobCreate } from "@/service/api-client/api-job";
import { uploadFileService } from "@/service/upload-service";
@@ -118,9 +119,10 @@ export function Job_ScreenCreate() {
};
return (
<PageWrapper
<OS_Wrapper
enableKeyboardHandling
keyboardScrollOffset={100}
keyboardScrollOffset={10}
contentPadding={PADDING_INLINE}
footerComponent={buttonSubmit()}
>
<StackCustom gap={"xs"}>
@@ -173,7 +175,9 @@ export function Job_ScreenCreate() {
onChangeText={(value) => setData({ ...data, deskripsi: value })}
/>
</View>
</StackCustom>
</PageWrapper>
</OS_Wrapper>
);
}

View File

@@ -8,13 +8,14 @@ import {
InformationBox,
LandscapeFrameUploaded,
LoaderCustom,
PageWrapper,
OS_Wrapper,
Spacing,
StackCustom,
TextAreaCustom,
TextInputCustom,
} from "@/components";
import DIRECTORY_ID from "@/constants/directory-id";
import { PADDING_INLINE } from "@/constants/constans-value";
import { apiJobGetOne, apiJobUpdateData } from "@/service/api-client/api-job";
import { deleteFileService, uploadFileService } from "@/service/upload-service";
import pickImage from "@/utils/pickImage";
@@ -134,9 +135,10 @@ export function Job_ScreenEdit() {
};
return (
<PageWrapper
<OS_Wrapper
enableKeyboardHandling
keyboardScrollOffset={100}
contentPadding={PADDING_INLINE}
footerComponent={buttonSubmit()}
>
{isLoadData ? (
@@ -203,6 +205,6 @@ export function Job_ScreenEdit() {
{buttonSubmit()}
</StackCustom>
)}
</PageWrapper>
</OS_Wrapper>
);
}