@jobber/components-native 0.95.2-JOB-141866-1ab1d84.4 → 0.95.2-JOB-141866-80fe04c.8
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/dist/package.json +2 -2
- package/dist/src/ContentOverlay/ContentOverlay.js +1 -2
- package/dist/src/Form/Form.js +12 -23
- package/dist/src/InputText/InputText.js +50 -2
- package/dist/src/InputText/context/InputAccessoriesContext.js +2 -0
- package/dist/src/InputText/context/InputAccessoriesProvider.js +6 -1
- package/dist/src/hooks/index.js +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/src/InputText/context/types.d.ts +2 -0
- package/dist/types/src/hooks/index.d.ts +1 -0
- package/package.json +2 -2
- package/src/ContentOverlay/ContentOverlay.tsx +1 -2
- package/src/Form/Form.tsx +21 -27
- package/src/InputText/InputText.tsx +35 -1
- package/src/InputText/context/InputAccessoriesContext.ts +2 -0
- package/src/InputText/context/InputAccessoriesProvider.tsx +7 -0
- package/src/InputText/context/types.ts +2 -0
- package/src/hooks/index.ts +1 -0
- /package/dist/src/{ContentOverlay/hooks → hooks}/useKeyboardVisibility.js +0 -0
- /package/dist/types/src/{ContentOverlay/hooks → hooks}/useKeyboardVisibility.d.ts +0 -0
- /package/src/{ContentOverlay/hooks → hooks}/useKeyboardVisibility.test.ts +0 -0
- /package/src/{ContentOverlay/hooks → hooks}/useKeyboardVisibility.ts +0 -0
|
@@ -19,5 +19,7 @@ export interface InputAccessoriesContextProps {
|
|
|
19
19
|
readonly onFocusNext: () => void;
|
|
20
20
|
readonly onFocusPrevious: () => void;
|
|
21
21
|
readonly setFocusedInput: (name: string) => void;
|
|
22
|
+
readonly isScrolling: boolean;
|
|
23
|
+
readonly setIsScrolling: (isScrolling: boolean) => void;
|
|
22
24
|
}
|
|
23
25
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jobber/components-native",
|
|
3
|
-
"version": "0.95.2-JOB-141866-
|
|
3
|
+
"version": "0.95.2-JOB-141866-80fe04c.8+80fe04c86",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "React Native implementation of Atlantis",
|
|
6
6
|
"repository": {
|
|
@@ -96,5 +96,5 @@
|
|
|
96
96
|
"react-native-safe-area-context": "^5.4.0",
|
|
97
97
|
"react-native-svg": ">=12.0.0"
|
|
98
98
|
},
|
|
99
|
-
"gitHead": "
|
|
99
|
+
"gitHead": "80fe04c86fc91e07591231460c32bc3194072bdc"
|
|
100
100
|
}
|
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
useWindowDimensions,
|
|
19
19
|
} from "react-native";
|
|
20
20
|
import { Portal } from "react-native-portalize";
|
|
21
|
-
import { useKeyboardVisibility } from "./hooks/useKeyboardVisibility";
|
|
22
21
|
import { useStyles } from "./ContentOverlay.style";
|
|
23
22
|
import { useViewLayoutHeight } from "./hooks/useViewLayoutHeight";
|
|
24
23
|
import type {
|
|
@@ -27,7 +26,7 @@ import type {
|
|
|
27
26
|
ModalBackgroundColor,
|
|
28
27
|
} from "./types";
|
|
29
28
|
import { UNSAFE_WrappedModalize } from "./UNSAFE_WrappedModalize";
|
|
30
|
-
import { useIsScreenReaderEnabled } from "../hooks";
|
|
29
|
+
import { useIsScreenReaderEnabled, useKeyboardVisibility } from "../hooks";
|
|
31
30
|
import { IconButton } from "../IconButton";
|
|
32
31
|
import { Heading } from "../Heading";
|
|
33
32
|
import { useAtlantisI18n } from "../hooks/useAtlantisI18n";
|
package/src/Form/Form.tsx
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
1
|
+
import React, { useMemo, useState } from "react";
|
|
2
2
|
import type { FieldValues } from "react-hook-form";
|
|
3
3
|
import { FormProvider } from "react-hook-form";
|
|
4
4
|
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
|
|
5
|
-
import type { LayoutChangeEvent } from "react-native";
|
|
6
5
|
import { Keyboard, Platform, View, findNodeHandle } from "react-native";
|
|
7
6
|
import { useStyles } from "./Form.style";
|
|
8
7
|
import { FormErrorBanner } from "./components/FormErrorBanner";
|
|
@@ -71,6 +70,7 @@ function InternalForm<T extends FieldValues, S>({
|
|
|
71
70
|
const { scrollViewRef, bottomViewRef, scrollToTop } = useFormViewRefs();
|
|
72
71
|
const [saveButtonHeight, setSaveButtonHeight] = useState(0);
|
|
73
72
|
const [messageBannerHeight, setMessageBannerHeight] = useState(0);
|
|
73
|
+
// const { setIsScrolling } = useInputAccessoriesContext();
|
|
74
74
|
const {
|
|
75
75
|
formMethods,
|
|
76
76
|
handleSubmit,
|
|
@@ -108,8 +108,12 @@ function InternalForm<T extends FieldValues, S>({
|
|
|
108
108
|
const [isSecondaryActionLoading, setIsSecondaryActionLoading] =
|
|
109
109
|
useState<boolean>(false);
|
|
110
110
|
|
|
111
|
-
const
|
|
112
|
-
|
|
111
|
+
const { calculatedKeyboardHeight } = useMemo(() => {
|
|
112
|
+
return {
|
|
113
|
+
calculatedKeyboardHeight:
|
|
114
|
+
keyboardHeight - (paddingBottom + KEYBOARD_SAVE_BUTTON_DISTANCE),
|
|
115
|
+
};
|
|
116
|
+
}, [paddingBottom, keyboardHeight]);
|
|
113
117
|
|
|
114
118
|
useScrollToError({
|
|
115
119
|
formState: formMethods.formState,
|
|
@@ -122,9 +126,8 @@ function InternalForm<T extends FieldValues, S>({
|
|
|
122
126
|
|
|
123
127
|
const keyboardProps = Platform.select({
|
|
124
128
|
ios: {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
onKeyboardDidChangeFrame: handleKeyboardDidChangeFrame,
|
|
129
|
+
onKeyboardWillHide: handleKeyboardHide,
|
|
130
|
+
onKeyboardWillShow: handleKeyboardShow,
|
|
128
131
|
},
|
|
129
132
|
android: {
|
|
130
133
|
onKeyboardDidHide: handleKeyboardHide,
|
|
@@ -132,10 +135,6 @@ function InternalForm<T extends FieldValues, S>({
|
|
|
132
135
|
},
|
|
133
136
|
});
|
|
134
137
|
|
|
135
|
-
const onLayout = (event: LayoutChangeEvent) => {
|
|
136
|
-
setMessageBannerHeight(event.nativeEvent.layout.height);
|
|
137
|
-
};
|
|
138
|
-
|
|
139
138
|
const styles = useStyles();
|
|
140
139
|
|
|
141
140
|
const { edgeToEdgeEnabled } = useAtlantisFormContext();
|
|
@@ -179,13 +178,23 @@ function InternalForm<T extends FieldValues, S>({
|
|
|
179
178
|
contentContainerStyle={
|
|
180
179
|
!keyboardHeight && styles.scrollContentContainer
|
|
181
180
|
}
|
|
181
|
+
// onScrollBeginDrag={() => {
|
|
182
|
+
// setIsScrolling(true);
|
|
183
|
+
// }}
|
|
184
|
+
// onScrollEndDrag={() => {
|
|
185
|
+
// setIsScrolling(false);
|
|
186
|
+
// }}
|
|
182
187
|
>
|
|
183
188
|
<View
|
|
184
189
|
onLayout={({ nativeEvent }) => {
|
|
185
190
|
setFormContentHeight(nativeEvent.layout.height);
|
|
186
191
|
}}
|
|
187
192
|
>
|
|
188
|
-
<View
|
|
193
|
+
<View
|
|
194
|
+
onLayout={({ nativeEvent }) => {
|
|
195
|
+
setMessageBannerHeight(nativeEvent.layout.height);
|
|
196
|
+
}}
|
|
197
|
+
>
|
|
189
198
|
<FormMessageBanner bannerMessages={bannerMessages} />
|
|
190
199
|
<FormErrorBanner
|
|
191
200
|
networkError={bannerErrors?.networkError}
|
|
@@ -230,21 +239,6 @@ function InternalForm<T extends FieldValues, S>({
|
|
|
230
239
|
</FormProvider>
|
|
231
240
|
);
|
|
232
241
|
|
|
233
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
234
|
-
function handleKeyboardDidChangeFrame(frames: Record<string, any>) {
|
|
235
|
-
if (
|
|
236
|
-
frames &&
|
|
237
|
-
"endCoordinates" in frames &&
|
|
238
|
-
"height" in frames.endCoordinates &&
|
|
239
|
-
typeof frames.endCoordinates.height === "number" &&
|
|
240
|
-
frames.endCoordinates.height > keyboardHeight
|
|
241
|
-
) {
|
|
242
|
-
handleKeyboardShow(frames);
|
|
243
|
-
} else {
|
|
244
|
-
handleKeyboardHide();
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
242
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
249
243
|
function handleKeyboardShow(frames: Record<string, any>) {
|
|
250
244
|
setKeyboardScreenY(frames.endCoordinates.screenY);
|
|
@@ -29,6 +29,15 @@ import type {
|
|
|
29
29
|
} from "../InputFieldWrapper/InputFieldWrapper";
|
|
30
30
|
import { InputFieldWrapper } from "../InputFieldWrapper";
|
|
31
31
|
import { useCommonInputStyles } from "../InputFieldWrapper/CommonInputStyles.style";
|
|
32
|
+
import { useScreenInformation } from "../Form/hooks/useScreenInformation";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Buffer zone in pixels for offscreen detection.
|
|
36
|
+
* This makes the detection more sensitive by marking the component as offscreen
|
|
37
|
+
* even if it's technically still visible but within this buffer distance from the edge.
|
|
38
|
+
*/
|
|
39
|
+
// 44 (accessory bar height) + 20 (buffer)
|
|
40
|
+
const KEYBOARD_AWARE_DETECTION_BUFFER = 64;
|
|
32
41
|
|
|
33
42
|
export interface InputTextProps
|
|
34
43
|
extends Pick<
|
|
@@ -372,6 +381,10 @@ function InputTextInternal(
|
|
|
372
381
|
|
|
373
382
|
const styles = useStyles();
|
|
374
383
|
const commonInputStyles = useCommonInputStyles();
|
|
384
|
+
// const { headerHeight, windowHeight } = useScreenInformation();
|
|
385
|
+
// State to track if the InputText component can fully fit on screen
|
|
386
|
+
// (i.e., it's completely visible). Use this state to handle visibility issues.
|
|
387
|
+
// const [canFullyFitOnScreen, setCanFullyFitOnScreen] = useState(true);
|
|
375
388
|
|
|
376
389
|
return (
|
|
377
390
|
<InputFieldWrapper
|
|
@@ -394,6 +407,20 @@ function InputTextInternal(
|
|
|
394
407
|
loadingType={loadingType}
|
|
395
408
|
>
|
|
396
409
|
<TextInput
|
|
410
|
+
// onLayout={(event: LayoutChangeEvent) => {
|
|
411
|
+
// event.target?.measureInWindow((_, y, __, height) => {
|
|
412
|
+
// // Check if component can't fully fit on screen (height only)
|
|
413
|
+
// // Account for headerHeight at the top of the screen and buffer zone
|
|
414
|
+
// const visibleTop = headerHeight + KEYBOARD_AWARE_DETECTION_BUFFER; // Top of visible area (below header) with buffer
|
|
415
|
+
// const visibleBottom =
|
|
416
|
+
// windowHeight - KEYBOARD_AWARE_DETECTION_BUFFER; // Bottom of visible area with buffer
|
|
417
|
+
// const isOffScreen =
|
|
418
|
+
// y < visibleTop || // Top edge is behind or above the header (with buffer)
|
|
419
|
+
// y + height > visibleBottom; // Bottom edge is below the window (with buffer)
|
|
420
|
+
|
|
421
|
+
// setCanFullyFitOnScreen(!isOffScreen);
|
|
422
|
+
// });
|
|
423
|
+
// }}
|
|
397
424
|
inputAccessoryViewID={inputAccessoryID || undefined}
|
|
398
425
|
testID={testID}
|
|
399
426
|
autoCapitalize={autoCapitalize}
|
|
@@ -413,6 +440,9 @@ function InputTextInternal(
|
|
|
413
440
|
styleOverride?.inputText,
|
|
414
441
|
loading && loadingType === "glimmer" && { color: "transparent" },
|
|
415
442
|
]}
|
|
443
|
+
// Prevent focus during scroll for multiline inputs to avoid
|
|
444
|
+
// the input focusing when the user is trying to scroll the form
|
|
445
|
+
// readOnly={readonly || (multiline && isScrolling && !focused)}
|
|
416
446
|
readOnly={readonly}
|
|
417
447
|
editable={!disabled}
|
|
418
448
|
keyboardType={keyboard}
|
|
@@ -420,7 +450,11 @@ function InputTextInternal(
|
|
|
420
450
|
autoFocus={autoFocus}
|
|
421
451
|
autoComplete={autoComplete}
|
|
422
452
|
multiline={multiline}
|
|
423
|
-
|
|
453
|
+
// Makes sure it doesn't jump to the top of the screen when the keyboard is shown and a new line is added.
|
|
454
|
+
// State for tracking if the input should be scrollable.
|
|
455
|
+
// This is tech debt related to an issue where keyboard aware scrollview doesn't work if `scrollEnabled` is true. However,
|
|
456
|
+
// when `scrollEnabled` is false it causes an issue where super long text inputs will jump to the top when a new line is added to the bottom of the input.
|
|
457
|
+
scrollEnabled={Platform.OS === "ios"}
|
|
424
458
|
textContentType={textContentType}
|
|
425
459
|
onChangeText={handleChangeText}
|
|
426
460
|
onSubmitEditing={handleOnSubmitEditing}
|
|
@@ -12,6 +12,8 @@ const inputAccessoriesContextDefaultValues: InputAccessoriesContextProps = {
|
|
|
12
12
|
onFocusNext: () => undefined,
|
|
13
13
|
onFocusPrevious: () => undefined,
|
|
14
14
|
setFocusedInput: () => undefined,
|
|
15
|
+
isScrolling: false,
|
|
16
|
+
setIsScrolling: () => undefined,
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
export const InputAccessoriesContext = createContext(
|
|
@@ -27,6 +27,8 @@ export function InputAccessoriesProvider({
|
|
|
27
27
|
setElements,
|
|
28
28
|
previousKey,
|
|
29
29
|
nextKey,
|
|
30
|
+
isScrolling,
|
|
31
|
+
setIsScrolling,
|
|
30
32
|
} = useInputAccessoriesProviderState();
|
|
31
33
|
|
|
32
34
|
const colorScheme = useColorScheme();
|
|
@@ -46,6 +48,8 @@ export function InputAccessoriesProvider({
|
|
|
46
48
|
onFocusNext,
|
|
47
49
|
onFocusPrevious,
|
|
48
50
|
setFocusedInput,
|
|
51
|
+
isScrolling,
|
|
52
|
+
setIsScrolling,
|
|
49
53
|
}}
|
|
50
54
|
>
|
|
51
55
|
{children}
|
|
@@ -96,6 +100,7 @@ function useInputAccessoriesProviderState() {
|
|
|
96
100
|
const [canFocusNext, setCanFocusNext] = useState(false);
|
|
97
101
|
const [canFocusPrevious, setCanFocusPrevious] = useState(false);
|
|
98
102
|
const [elements, setElements] = useState<Record<string, () => void>>({});
|
|
103
|
+
const [isScrolling, setIsScrolling] = useState(false);
|
|
99
104
|
|
|
100
105
|
const keys = Object.keys(elements);
|
|
101
106
|
const selectedIndex = keys.findIndex(key => key === focusedInput);
|
|
@@ -119,5 +124,7 @@ function useInputAccessoriesProviderState() {
|
|
|
119
124
|
setCanFocusPrevious,
|
|
120
125
|
elements,
|
|
121
126
|
setElements,
|
|
127
|
+
isScrolling,
|
|
128
|
+
setIsScrolling,
|
|
122
129
|
};
|
|
123
130
|
}
|
|
@@ -25,4 +25,6 @@ export interface InputAccessoriesContextProps {
|
|
|
25
25
|
readonly onFocusNext: () => void;
|
|
26
26
|
readonly onFocusPrevious: () => void;
|
|
27
27
|
readonly setFocusedInput: (name: string) => void;
|
|
28
|
+
readonly isScrolling: boolean;
|
|
29
|
+
readonly setIsScrolling: (isScrolling: boolean) => void;
|
|
28
30
|
}
|
package/src/hooks/index.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|