@jobber/components-native 0.95.2-JOB-141866-990fa70.9 → 0.95.2-JOB-141866-ccf6ba8.10
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/Form/Form.js +2 -7
- package/dist/src/InputFieldWrapper/InputFieldWrapper.js +2 -2
- package/dist/src/InputText/InputText.js +29 -44
- package/dist/src/InputText/context/InputAccessoriesContext.js +0 -2
- package/dist/src/InputText/context/InputAccessoriesProvider.js +1 -6
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/src/InputFieldWrapper/InputFieldWrapper.d.ts +3 -2
- package/dist/types/src/InputText/InputText.d.ts +0 -5
- package/dist/types/src/InputText/context/types.d.ts +0 -2
- package/package.json +2 -2
- package/src/Form/Form.tsx +1 -11
- package/src/InputFieldWrapper/InputFieldWrapper.tsx +10 -1
- package/src/InputText/InputText.tsx +22 -26
- package/src/InputText/context/InputAccessoriesContext.ts +0 -2
- package/src/InputText/context/InputAccessoriesProvider.test.tsx +1 -0
- package/src/InputText/context/InputAccessoriesProvider.tsx +0 -7
- package/src/InputText/context/types.ts +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import type { StyleProp, TextStyle, ViewStyle } from "react-native";
|
|
2
|
+
import type { LayoutChangeEvent, StyleProp, TextStyle, ViewStyle } from "react-native";
|
|
3
3
|
import type { FieldError } from "react-hook-form";
|
|
4
4
|
import type { IconNames } from "@jobber/design";
|
|
5
5
|
export type Clearable = "never" | "while-editing" | "always";
|
|
@@ -82,7 +82,8 @@ export interface InputFieldWrapperProps {
|
|
|
82
82
|
* Change the type of loading indicator to spinner or glimmer.
|
|
83
83
|
*/
|
|
84
84
|
readonly loadingType?: "spinner" | "glimmer";
|
|
85
|
+
readonly scrollViewHackOnLayout?: (event: LayoutChangeEvent) => void;
|
|
85
86
|
}
|
|
86
87
|
export declare const INPUT_FIELD_WRAPPER_GLIMMERS_TEST_ID = "ATL-InputFieldWrapper-Glimmers";
|
|
87
88
|
export declare const INPUT_FIELD_WRAPPER_SPINNER_TEST_ID = "ATL-InputFieldWrapper-Spinner";
|
|
88
|
-
export declare function InputFieldWrapper({ invalid, disabled, placeholder, assistiveText, prefix, suffix, placeholderMode, hasValue, error, focused, children, onClear, showClearAction, styleOverride, toolbar, toolbarVisibility, loading, loadingType, }: InputFieldWrapperProps): React.JSX.Element;
|
|
89
|
+
export declare function InputFieldWrapper({ invalid, disabled, placeholder, assistiveText, prefix, suffix, placeholderMode, hasValue, error, focused, children, onClear, showClearAction, styleOverride, toolbar, toolbarVisibility, loading, loadingType, scrollViewHackOnLayout, }: InputFieldWrapperProps): React.JSX.Element;
|
|
@@ -6,11 +6,6 @@ import type { RegisterOptions } from "react-hook-form";
|
|
|
6
6
|
import type { IconNames } from "@jobber/design";
|
|
7
7
|
import type { Clearable } from "@jobber/hooks";
|
|
8
8
|
import type { InputFieldStyleOverride, InputFieldWrapperProps } from "../InputFieldWrapper/InputFieldWrapper";
|
|
9
|
-
/**
|
|
10
|
-
* Buffer zone in pixels for offscreen detection.
|
|
11
|
-
* This makes the detection more sensitive by marking the component as offscreen
|
|
12
|
-
* even if it's technically still visible but within this buffer distance from the edge.
|
|
13
|
-
*/
|
|
14
9
|
export interface InputTextProps extends Pick<InputFieldWrapperProps, "toolbar" | "toolbarVisibility" | "loading" | "loadingType"> {
|
|
15
10
|
/**
|
|
16
11
|
* Highlights the field red and shows message below (if string) to indicate an error
|
|
@@ -19,7 +19,5 @@ 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;
|
|
24
22
|
}
|
|
25
23
|
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-ccf6ba8.10+ccf6ba8d6",
|
|
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": "ccf6ba8d6a687be9144debdfeab7c09525685fb0"
|
|
100
100
|
}
|
package/src/Form/Form.tsx
CHANGED
|
@@ -26,10 +26,7 @@ import { FormSaveButton } from "./components/FormSaveButton";
|
|
|
26
26
|
import { useSaveButtonPosition } from "./hooks/useSaveButtonPosition";
|
|
27
27
|
import { FormCache } from "./components/FormCache/FormCache";
|
|
28
28
|
import { useAtlantisFormContext } from "./context/AtlantisFormContext";
|
|
29
|
-
import {
|
|
30
|
-
InputAccessoriesProvider,
|
|
31
|
-
useInputAccessoriesContext,
|
|
32
|
-
} from "../InputText";
|
|
29
|
+
import { InputAccessoriesProvider } from "../InputText";
|
|
33
30
|
import { tokens } from "../utils/design";
|
|
34
31
|
import { ErrorMessageProvider } from "../ErrorMessageWrapper";
|
|
35
32
|
|
|
@@ -73,7 +70,6 @@ function InternalForm<T extends FieldValues, S>({
|
|
|
73
70
|
const { scrollViewRef, bottomViewRef, scrollToTop } = useFormViewRefs();
|
|
74
71
|
const [saveButtonHeight, setSaveButtonHeight] = useState(0);
|
|
75
72
|
const [messageBannerHeight, setMessageBannerHeight] = useState(0);
|
|
76
|
-
const { setIsScrolling } = useInputAccessoriesContext();
|
|
77
73
|
const {
|
|
78
74
|
formMethods,
|
|
79
75
|
handleSubmit,
|
|
@@ -181,12 +177,6 @@ function InternalForm<T extends FieldValues, S>({
|
|
|
181
177
|
contentContainerStyle={
|
|
182
178
|
!keyboardHeight && styles.scrollContentContainer
|
|
183
179
|
}
|
|
184
|
-
onScrollBeginDrag={() => {
|
|
185
|
-
setIsScrolling(true);
|
|
186
|
-
}}
|
|
187
|
-
onScrollEndDrag={() => {
|
|
188
|
-
setIsScrolling(false);
|
|
189
|
-
}}
|
|
190
180
|
>
|
|
191
181
|
<View
|
|
192
182
|
onLayout={({ nativeEvent }) => {
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
LayoutChangeEvent,
|
|
4
|
+
StyleProp,
|
|
5
|
+
TextStyle,
|
|
6
|
+
ViewStyle,
|
|
7
|
+
} from "react-native";
|
|
3
8
|
import { Text as RNText, View } from "react-native";
|
|
4
9
|
import type { FieldError } from "react-hook-form";
|
|
5
10
|
import type { IconNames } from "@jobber/design";
|
|
@@ -112,6 +117,8 @@ export interface InputFieldWrapperProps {
|
|
|
112
117
|
* Change the type of loading indicator to spinner or glimmer.
|
|
113
118
|
*/
|
|
114
119
|
readonly loadingType?: "spinner" | "glimmer";
|
|
120
|
+
|
|
121
|
+
readonly scrollViewHackOnLayout?: (event: LayoutChangeEvent) => void;
|
|
115
122
|
}
|
|
116
123
|
|
|
117
124
|
export const INPUT_FIELD_WRAPPER_GLIMMERS_TEST_ID =
|
|
@@ -138,6 +145,7 @@ export function InputFieldWrapper({
|
|
|
138
145
|
toolbarVisibility = "while-editing",
|
|
139
146
|
loading = false,
|
|
140
147
|
loadingType = "spinner",
|
|
148
|
+
scrollViewHackOnLayout,
|
|
141
149
|
}: InputFieldWrapperProps) {
|
|
142
150
|
fieldAffixRequiredPropsCheck([prefix, suffix]);
|
|
143
151
|
const handleClear = onClear ?? noopClear;
|
|
@@ -164,6 +172,7 @@ export function InputFieldWrapper({
|
|
|
164
172
|
disabled && styles.disabled,
|
|
165
173
|
styleOverride?.container,
|
|
166
174
|
]}
|
|
175
|
+
onLayout={scrollViewHackOnLayout}
|
|
167
176
|
>
|
|
168
177
|
<View style={styles.field}>
|
|
169
178
|
{prefix?.icon && (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Ref, SyntheticEvent } from "react";
|
|
2
2
|
import React, {
|
|
3
3
|
forwardRef,
|
|
4
|
+
useDeferredValue,
|
|
4
5
|
useEffect,
|
|
5
6
|
useImperativeHandle,
|
|
6
7
|
useMemo,
|
|
@@ -22,13 +23,14 @@ import type { Clearable } from "@jobber/hooks";
|
|
|
22
23
|
import { useShowClear } from "@jobber/hooks";
|
|
23
24
|
import { useStyles } from "./InputText.style";
|
|
24
25
|
import { useInputAccessoriesContext } from "./context";
|
|
25
|
-
import { useFormController } from "../hooks";
|
|
26
|
+
import { useFormController, useKeyboardVisibility } from "../hooks";
|
|
26
27
|
import type {
|
|
27
28
|
InputFieldStyleOverride,
|
|
28
29
|
InputFieldWrapperProps,
|
|
29
30
|
} from "../InputFieldWrapper/InputFieldWrapper";
|
|
30
31
|
import { InputFieldWrapper } from "../InputFieldWrapper";
|
|
31
32
|
import { useCommonInputStyles } from "../InputFieldWrapper/CommonInputStyles.style";
|
|
33
|
+
import { useScreenInformation } from "../Form/hooks/useScreenInformation";
|
|
32
34
|
// import { useScreenInformation } from "../Form/hooks/useScreenInformation";
|
|
33
35
|
|
|
34
36
|
/**
|
|
@@ -37,7 +39,7 @@ import { useCommonInputStyles } from "../InputFieldWrapper/CommonInputStyles.sty
|
|
|
37
39
|
* even if it's technically still visible but within this buffer distance from the edge.
|
|
38
40
|
*/
|
|
39
41
|
// 44 (accessory bar height) + 20 (buffer)
|
|
40
|
-
|
|
42
|
+
const KEYBOARD_AWARE_DETECTION_BUFFER = 40;
|
|
41
43
|
|
|
42
44
|
export interface InputTextProps
|
|
43
45
|
extends Pick<
|
|
@@ -341,7 +343,6 @@ function InputTextInternal(
|
|
|
341
343
|
unregister,
|
|
342
344
|
setFocusedInput,
|
|
343
345
|
canFocusNext,
|
|
344
|
-
isScrolling,
|
|
345
346
|
onFocusNext,
|
|
346
347
|
} = useInputAccessoriesContext();
|
|
347
348
|
useEffect(() => {
|
|
@@ -382,13 +383,25 @@ function InputTextInternal(
|
|
|
382
383
|
|
|
383
384
|
const styles = useStyles();
|
|
384
385
|
const commonInputStyles = useCommonInputStyles();
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
386
|
+
const { headerHeight, windowHeight } = useScreenInformation();
|
|
387
|
+
const { keyboardHeight } = useKeyboardVisibility();
|
|
388
|
+
const maxHeight = useDeferredValue(
|
|
389
|
+
windowHeight -
|
|
390
|
+
headerHeight -
|
|
391
|
+
keyboardHeight -
|
|
392
|
+
KEYBOARD_AWARE_DETECTION_BUFFER,
|
|
393
|
+
);
|
|
394
|
+
const [inputHeight, setInputHeight] = useState(0);
|
|
395
|
+
|
|
396
|
+
const enableScroll = useDeferredValue(inputHeight > maxHeight);
|
|
389
397
|
|
|
390
398
|
return (
|
|
391
399
|
<InputFieldWrapper
|
|
400
|
+
scrollViewHackOnLayout={event => {
|
|
401
|
+
event.target?.measureInWindow((_, y, __, height) => {
|
|
402
|
+
setInputHeight(height);
|
|
403
|
+
});
|
|
404
|
+
}}
|
|
392
405
|
prefix={prefix}
|
|
393
406
|
suffix={suffix}
|
|
394
407
|
hasValue={hasValue}
|
|
@@ -408,20 +421,6 @@ function InputTextInternal(
|
|
|
408
421
|
loadingType={loadingType}
|
|
409
422
|
>
|
|
410
423
|
<TextInput
|
|
411
|
-
// onLayout={(event: LayoutChangeEvent) => {
|
|
412
|
-
// event.target?.measureInWindow((_, y, __, height) => {
|
|
413
|
-
// // Check if component can't fully fit on screen (height only)
|
|
414
|
-
// // Account for headerHeight at the top of the screen and buffer zone
|
|
415
|
-
// const visibleTop = headerHeight + KEYBOARD_AWARE_DETECTION_BUFFER; // Top of visible area (below header) with buffer
|
|
416
|
-
// const visibleBottom =
|
|
417
|
-
// windowHeight - KEYBOARD_AWARE_DETECTION_BUFFER; // Bottom of visible area with buffer
|
|
418
|
-
// const isOffScreen =
|
|
419
|
-
// y < visibleTop || // Top edge is behind or above the header (with buffer)
|
|
420
|
-
// y + height > visibleBottom; // Bottom edge is below the window (with buffer)
|
|
421
|
-
|
|
422
|
-
// setCanFullyFitOnScreen(!isOffScreen);
|
|
423
|
-
// });
|
|
424
|
-
// }}
|
|
425
424
|
inputAccessoryViewID={inputAccessoryID || undefined}
|
|
426
425
|
testID={testID}
|
|
427
426
|
autoCapitalize={autoCapitalize}
|
|
@@ -441,10 +440,7 @@ function InputTextInternal(
|
|
|
441
440
|
styleOverride?.inputText,
|
|
442
441
|
loading && loadingType === "glimmer" && { color: "transparent" },
|
|
443
442
|
]}
|
|
444
|
-
|
|
445
|
-
// the input focusing when the user is trying to scroll the form
|
|
446
|
-
readOnly={readonly || (multiline && isScrolling && !focused)}
|
|
447
|
-
// readOnly={readonly}
|
|
443
|
+
readOnly={readonly}
|
|
448
444
|
editable={!disabled}
|
|
449
445
|
keyboardType={keyboard}
|
|
450
446
|
value={inputTransform(internalValue)}
|
|
@@ -455,7 +451,7 @@ function InputTextInternal(
|
|
|
455
451
|
// State for tracking if the input should be scrollable.
|
|
456
452
|
// This is tech debt related to an issue where keyboard aware scrollview doesn't work if `scrollEnabled` is true. However,
|
|
457
453
|
// 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.
|
|
458
|
-
scrollEnabled={Platform.OS === "ios"}
|
|
454
|
+
scrollEnabled={Platform.OS === "ios" && enableScroll}
|
|
459
455
|
textContentType={textContentType}
|
|
460
456
|
onChangeText={handleChangeText}
|
|
461
457
|
onSubmitEditing={handleOnSubmitEditing}
|
|
@@ -12,8 +12,6 @@ const inputAccessoriesContextDefaultValues: InputAccessoriesContextProps = {
|
|
|
12
12
|
onFocusNext: () => undefined,
|
|
13
13
|
onFocusPrevious: () => undefined,
|
|
14
14
|
setFocusedInput: () => undefined,
|
|
15
|
-
isScrolling: false,
|
|
16
|
-
setIsScrolling: () => undefined,
|
|
17
15
|
};
|
|
18
16
|
|
|
19
17
|
export const InputAccessoriesContext = createContext(
|
|
@@ -8,6 +8,7 @@ import { InputText } from "../InputText";
|
|
|
8
8
|
const mockUseFormController = jest.fn();
|
|
9
9
|
jest.mock("../../hooks", () => {
|
|
10
10
|
return {
|
|
11
|
+
...jest.requireActual("../../hooks"),
|
|
11
12
|
useFormController: (
|
|
12
13
|
...args: [{ name: string; value: string; validations: unknown }]
|
|
13
14
|
) => mockUseFormController(...args),
|
|
@@ -27,8 +27,6 @@ export function InputAccessoriesProvider({
|
|
|
27
27
|
setElements,
|
|
28
28
|
previousKey,
|
|
29
29
|
nextKey,
|
|
30
|
-
isScrolling,
|
|
31
|
-
setIsScrolling,
|
|
32
30
|
} = useInputAccessoriesProviderState();
|
|
33
31
|
|
|
34
32
|
const colorScheme = useColorScheme();
|
|
@@ -48,8 +46,6 @@ export function InputAccessoriesProvider({
|
|
|
48
46
|
onFocusNext,
|
|
49
47
|
onFocusPrevious,
|
|
50
48
|
setFocusedInput,
|
|
51
|
-
isScrolling,
|
|
52
|
-
setIsScrolling,
|
|
53
49
|
}}
|
|
54
50
|
>
|
|
55
51
|
{children}
|
|
@@ -100,7 +96,6 @@ function useInputAccessoriesProviderState() {
|
|
|
100
96
|
const [canFocusNext, setCanFocusNext] = useState(false);
|
|
101
97
|
const [canFocusPrevious, setCanFocusPrevious] = useState(false);
|
|
102
98
|
const [elements, setElements] = useState<Record<string, () => void>>({});
|
|
103
|
-
const [isScrolling, setIsScrolling] = useState(false);
|
|
104
99
|
|
|
105
100
|
const keys = Object.keys(elements);
|
|
106
101
|
const selectedIndex = keys.findIndex(key => key === focusedInput);
|
|
@@ -124,7 +119,5 @@ function useInputAccessoriesProviderState() {
|
|
|
124
119
|
setCanFocusPrevious,
|
|
125
120
|
elements,
|
|
126
121
|
setElements,
|
|
127
|
-
isScrolling,
|
|
128
|
-
setIsScrolling,
|
|
129
122
|
};
|
|
130
123
|
}
|
|
@@ -25,6 +25,4 @@ 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;
|
|
30
28
|
}
|