@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.95.2-JOB-141866-990fa70.9+990fa7081",
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": "990fa708191391b1d9f973c5eccd4dd8ec532424"
99
+ "gitHead": "ccf6ba8d6a687be9144debdfeab7c09525685fb0"
100
100
  }
@@ -39,7 +39,7 @@ import { FormSaveButton } from "./components/FormSaveButton";
39
39
  import { useSaveButtonPosition } from "./hooks/useSaveButtonPosition";
40
40
  import { FormCache } from "./components/FormCache/FormCache";
41
41
  import { useAtlantisFormContext } from "./context/AtlantisFormContext";
42
- import { InputAccessoriesProvider, useInputAccessoriesContext, } from "../InputText";
42
+ import { InputAccessoriesProvider } from "../InputText";
43
43
  import { tokens } from "../utils/design";
44
44
  import { ErrorMessageProvider } from "../ErrorMessageWrapper";
45
45
  export function Form(_a) {
@@ -54,7 +54,6 @@ function InternalForm({ children, onBeforeSubmit, onSubmit, onSubmitError, onSub
54
54
  const { scrollViewRef, bottomViewRef, scrollToTop } = useFormViewRefs();
55
55
  const [saveButtonHeight, setSaveButtonHeight] = useState(0);
56
56
  const [messageBannerHeight, setMessageBannerHeight] = useState(0);
57
- const { setIsScrolling } = useInputAccessoriesContext();
58
57
  const { formMethods, handleSubmit, isSubmitting, removeListenerRef, setLocalCache, } = useInternalForm({
59
58
  mode,
60
59
  reValidateMode,
@@ -111,11 +110,7 @@ function InternalForm({ children, onBeforeSubmit, onSubmit, onSubmitError, onSub
111
110
  (isSubmitting || isSecondaryActionLoading) && React.createElement(FormMask, null),
112
111
  React.createElement(FormCache, { localCacheKey: localCacheKey, localCacheExclude: localCacheExclude, setLocalCache: setLocalCache }),
113
112
  React.createElement(FormBody, { keyboardHeight: calculateSaveButtonOffset(), submit: handleSubmit(internalSubmit), isFormSubmitting: isSubmitting, saveButtonLabel: saveButtonLabel, shouldRenderActionBar: saveButtonPosition === "sticky", renderStickySection: renderStickySection, secondaryActions: secondaryActions, setSecondaryActionLoading: setIsSecondaryActionLoading, setSaveButtonHeight: setSaveButtonHeight, saveButtonOffset: saveButtonOffset },
114
- React.createElement(KeyboardAwareScrollView, Object.assign({ enableResetScrollToCoords: false, enableAutomaticScroll: true, enableOnAndroid: edgeToEdgeEnabled, keyboardOpeningTime: Platform.OS === "ios" ? tokens["timing-slowest"] : 0, keyboardShouldPersistTaps: "handled", ref: scrollViewRef }, keyboardProps, { extraHeight: headerHeight, extraScrollHeight: edgeToEdgeEnabled ? tokens["space-large"] : 0, contentContainerStyle: !keyboardHeight && styles.scrollContentContainer, onScrollBeginDrag: () => {
115
- setIsScrolling(true);
116
- }, onScrollEndDrag: () => {
117
- setIsScrolling(false);
118
- } }),
113
+ React.createElement(KeyboardAwareScrollView, Object.assign({ enableResetScrollToCoords: false, enableAutomaticScroll: true, enableOnAndroid: edgeToEdgeEnabled, keyboardOpeningTime: Platform.OS === "ios" ? tokens["timing-slowest"] : 0, keyboardShouldPersistTaps: "handled", ref: scrollViewRef }, keyboardProps, { extraHeight: headerHeight, extraScrollHeight: edgeToEdgeEnabled ? tokens["space-large"] : 0, contentContainerStyle: !keyboardHeight && styles.scrollContentContainer }),
119
114
  React.createElement(View, { onLayout: ({ nativeEvent }) => {
120
115
  setFormContentHeight(nativeEvent.layout.height);
121
116
  } },
@@ -11,7 +11,7 @@ import { Text } from "../Text";
11
11
  import { ActivityIndicator } from "../ActivityIndicator";
12
12
  export const INPUT_FIELD_WRAPPER_GLIMMERS_TEST_ID = "ATL-InputFieldWrapper-Glimmers";
13
13
  export const INPUT_FIELD_WRAPPER_SPINNER_TEST_ID = "ATL-InputFieldWrapper-Spinner";
14
- export function InputFieldWrapper({ invalid, disabled, placeholder, assistiveText, prefix, suffix, placeholderMode = "normal", hasValue = false, error, focused = false, children, onClear, showClearAction = false, styleOverride, toolbar, toolbarVisibility = "while-editing", loading = false, loadingType = "spinner", }) {
14
+ export function InputFieldWrapper({ invalid, disabled, placeholder, assistiveText, prefix, suffix, placeholderMode = "normal", hasValue = false, error, focused = false, children, onClear, showClearAction = false, styleOverride, toolbar, toolbarVisibility = "while-editing", loading = false, loadingType = "spinner", scrollViewHackOnLayout, }) {
15
15
  fieldAffixRequiredPropsCheck([prefix, suffix]);
16
16
  const handleClear = onClear !== null && onClear !== void 0 ? onClear : noopClear;
17
17
  warnIfClearActionWithNoOnClear(onClear, showClearAction);
@@ -29,7 +29,7 @@ export function InputFieldWrapper({ invalid, disabled, placeholder, assistiveTex
29
29
  (Boolean(invalid) || error) && styles.inputInvalid,
30
30
  disabled && styles.disabled,
31
31
  styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.container,
32
- ] },
32
+ ], onLayout: scrollViewHackOnLayout },
33
33
  React.createElement(View, { style: styles.field },
34
34
  (prefix === null || prefix === void 0 ? void 0 : prefix.icon) && (React.createElement(PrefixIcon, { disabled: disabled, focused: focused, inputInvalid: inputInvalid, icon: prefix.icon })),
35
35
  React.createElement(View, { style: [styles.inputContainer] },
@@ -1,12 +1,21 @@
1
- import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState, } from "react";
1
+ import React, { forwardRef, useDeferredValue, useEffect, useImperativeHandle, useMemo, useRef, useState, } from "react";
2
2
  import { Platform, TextInput } from "react-native";
3
3
  import identity from "lodash/identity";
4
4
  import { useShowClear } from "@jobber/hooks";
5
5
  import { useStyles } from "./InputText.style";
6
6
  import { useInputAccessoriesContext } from "./context";
7
- import { useFormController } from "../hooks";
7
+ import { useFormController, useKeyboardVisibility } from "../hooks";
8
8
  import { InputFieldWrapper } from "../InputFieldWrapper";
9
9
  import { useCommonInputStyles } from "../InputFieldWrapper/CommonInputStyles.style";
10
+ import { useScreenInformation } from "../Form/hooks/useScreenInformation";
11
+ // import { useScreenInformation } from "../Form/hooks/useScreenInformation";
12
+ /**
13
+ * Buffer zone in pixels for offscreen detection.
14
+ * This makes the detection more sensitive by marking the component as offscreen
15
+ * even if it's technically still visible but within this buffer distance from the edge.
16
+ */
17
+ // 44 (accessory bar height) + 20 (buffer)
18
+ const KEYBOARD_AWARE_DETECTION_BUFFER = 40;
10
19
  export const InputText = forwardRef(InputTextInternal);
11
20
  // eslint-disable-next-line max-statements
12
21
  function InputTextInternal({ invalid, disabled, readonly = false, name, placeholder, assistiveText, showMiniLabel = true, keyboard, value: controlledValue, defaultValue, autoFocus, autoComplete = "off", spellCheck, textContentType = "none", validations, onChangeText, onSubmitEditing, onFocus, accessibilityLabel, accessibilityHint, autoCorrect, autoCapitalize, onBlur, multiline = false, prefix, suffix, transform = {}, clearable = multiline ? "never" : "while-editing", testID, secureTextEntry, styleOverride, toolbar, toolbarVisibility, loading, loadingType, }, ref) {
@@ -40,7 +49,7 @@ function InputTextInternal({ invalid, disabled, readonly = false, name, placehol
40
49
  placeholderTextColor: "transparent",
41
50
  }));
42
51
  const _name = name !== null && name !== void 0 ? name : field.name;
43
- const { inputAccessoryID, register, unregister, setFocusedInput, canFocusNext, isScrolling, onFocusNext, } = useInputAccessoriesContext();
52
+ const { inputAccessoryID, register, unregister, setFocusedInput, canFocusNext, onFocusNext, } = useInputAccessoriesContext();
44
53
  useEffect(() => {
45
54
  _name &&
46
55
  register(_name, () => {
@@ -75,40 +84,21 @@ function InputTextInternal({ invalid, disabled, readonly = false, name, placehol
75
84
  }
76
85
  const styles = useStyles();
77
86
  const commonInputStyles = useCommonInputStyles();
78
- // const { headerHeight, windowHeight } = useScreenInformation();
79
- // State to track if the InputText component can fully fit on screen
80
- // (i.e., it's completely visible). Use this state to handle visibility issues.
81
- // const [canFullyFitOnScreen, setCanFullyFitOnScreen] = useState(true);
82
- return (React.createElement(InputFieldWrapper, { prefix: prefix, suffix: suffix, hasValue: hasValue, placeholderMode: placeholderMode, assistiveText: assistiveText, focused: focused, error: error, invalid: invalid, placeholder: placeholder, disabled: disabled, onClear: handleClear, showClearAction: showClear, styleOverride: styleOverride, toolbar: toolbar, toolbarVisibility: toolbarVisibility, loading: loading, loadingType: loadingType },
83
- React.createElement(TextInput
84
- // onLayout={(event: LayoutChangeEvent) => {
85
- // event.target?.measureInWindow((_, y, __, height) => {
86
- // // Check if component can't fully fit on screen (height only)
87
- // // Account for headerHeight at the top of the screen and buffer zone
88
- // const visibleTop = headerHeight + KEYBOARD_AWARE_DETECTION_BUFFER; // Top of visible area (below header) with buffer
89
- // const visibleBottom =
90
- // windowHeight - KEYBOARD_AWARE_DETECTION_BUFFER; // Bottom of visible area with buffer
91
- // const isOffScreen =
92
- // y < visibleTop || // Top edge is behind or above the header (with buffer)
93
- // y + height > visibleBottom; // Bottom edge is below the window (with buffer)
94
- // setCanFullyFitOnScreen(!isOffScreen);
95
- // });
96
- // }}
97
- , Object.assign({
98
- // onLayout={(event: LayoutChangeEvent) => {
99
- // event.target?.measureInWindow((_, y, __, height) => {
100
- // // Check if component can't fully fit on screen (height only)
101
- // // Account for headerHeight at the top of the screen and buffer zone
102
- // const visibleTop = headerHeight + KEYBOARD_AWARE_DETECTION_BUFFER; // Top of visible area (below header) with buffer
103
- // const visibleBottom =
104
- // windowHeight - KEYBOARD_AWARE_DETECTION_BUFFER; // Bottom of visible area with buffer
105
- // const isOffScreen =
106
- // y < visibleTop || // Top edge is behind or above the header (with buffer)
107
- // y + height > visibleBottom; // Bottom edge is below the window (with buffer)
108
- // setCanFullyFitOnScreen(!isOffScreen);
109
- // });
110
- // }}
111
- inputAccessoryViewID: inputAccessoryID || undefined, testID: testID, autoCapitalize: autoCapitalize, autoCorrect: autoCorrect, spellCheck: spellCheck, style: [
87
+ const { headerHeight, windowHeight } = useScreenInformation();
88
+ const { keyboardHeight } = useKeyboardVisibility();
89
+ const maxHeight = useDeferredValue(windowHeight -
90
+ headerHeight -
91
+ keyboardHeight -
92
+ KEYBOARD_AWARE_DETECTION_BUFFER);
93
+ const [inputHeight, setInputHeight] = useState(0);
94
+ const enableScroll = useDeferredValue(inputHeight > maxHeight);
95
+ return (React.createElement(InputFieldWrapper, { scrollViewHackOnLayout: event => {
96
+ var _a;
97
+ (_a = event.target) === null || _a === void 0 ? void 0 : _a.measureInWindow((_, y, __, height) => {
98
+ setInputHeight(height);
99
+ });
100
+ }, prefix: prefix, suffix: suffix, hasValue: hasValue, placeholderMode: placeholderMode, assistiveText: assistiveText, focused: focused, error: error, invalid: invalid, placeholder: placeholder, disabled: disabled, onClear: handleClear, showClearAction: showClear, styleOverride: styleOverride, toolbar: toolbar, toolbarVisibility: toolbarVisibility, loading: loading, loadingType: loadingType },
101
+ React.createElement(TextInput, Object.assign({ inputAccessoryViewID: inputAccessoryID || undefined, testID: testID, autoCapitalize: autoCapitalize, autoCorrect: autoCorrect, spellCheck: spellCheck, style: [
112
102
  commonInputStyles.input,
113
103
  styles.inputPaddingTop,
114
104
  !miniLabelActive && commonInputStyles.inputEmpty,
@@ -121,17 +111,12 @@ function InputTextInternal({ invalid, disabled, readonly = false, name, placehol
121
111
  styles.multilineWithoutMiniLabel,
122
112
  styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.inputText,
123
113
  loading && loadingType === "glimmer" && { color: "transparent" },
124
- ],
125
- // Prevent focus during scroll for multiline inputs to avoid
126
- // the input focusing when the user is trying to scroll the form
127
- readOnly: readonly || (multiline && isScrolling && !focused),
128
- // readOnly={readonly}
129
- editable: !disabled, keyboardType: keyboard, value: inputTransform(internalValue), autoFocus: autoFocus, autoComplete: autoComplete, multiline: multiline,
114
+ ], readOnly: readonly, editable: !disabled, keyboardType: keyboard, value: inputTransform(internalValue), autoFocus: autoFocus, autoComplete: autoComplete, multiline: multiline,
130
115
  // Makes sure it doesn't jump to the top of the screen when the keyboard is shown and a new line is added.
131
116
  // State for tracking if the input should be scrollable.
132
117
  // This is tech debt related to an issue where keyboard aware scrollview doesn't work if `scrollEnabled` is true. However,
133
118
  // 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.
134
- scrollEnabled: Platform.OS === "ios", textContentType: textContentType, onChangeText: handleChangeText, onSubmitEditing: handleOnSubmitEditing, returnKeyType: returnKeyType, blurOnSubmit: shouldBlurOnSubmit, accessibilityLabel: accessibilityLabel || placeholder, accessibilityHint: accessibilityHint, accessibilityState: { busy: loading }, secureTextEntry: secureTextEntry }, androidA11yProps, { onFocus: event => {
119
+ scrollEnabled: Platform.OS === "ios" && enableScroll, textContentType: textContentType, onChangeText: handleChangeText, onSubmitEditing: handleOnSubmitEditing, returnKeyType: returnKeyType, blurOnSubmit: shouldBlurOnSubmit, accessibilityLabel: accessibilityLabel || placeholder, accessibilityHint: accessibilityHint, accessibilityState: { busy: loading }, secureTextEntry: secureTextEntry }, androidA11yProps, { onFocus: event => {
135
120
  _name && setFocusedInput(_name);
136
121
  setFocused(true);
137
122
  onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
@@ -10,8 +10,6 @@ const inputAccessoriesContextDefaultValues = {
10
10
  onFocusNext: () => undefined,
11
11
  onFocusPrevious: () => undefined,
12
12
  setFocusedInput: () => undefined,
13
- isScrolling: false,
14
- setIsScrolling: () => undefined,
15
13
  };
16
14
  export const InputAccessoriesContext = createContext(inputAccessoriesContextDefaultValues);
17
15
  export function useInputAccessoriesContext() {
@@ -5,7 +5,7 @@ import { InputAccessoriesContext } from "./InputAccessoriesContext";
5
5
  import { useStyles } from "./InputAccessoriesProvider.style";
6
6
  export function InputAccessoriesProvider({ children, }) {
7
7
  const inputAccessoryID = useRef(v4()).current;
8
- const { focusedInput, setFocusedInput, canFocusNext, canFocusPrevious, elements, setElements, previousKey, nextKey, isScrolling, setIsScrolling, } = useInputAccessoriesProviderState();
8
+ const { focusedInput, setFocusedInput, canFocusNext, canFocusPrevious, elements, setElements, previousKey, nextKey, } = useInputAccessoriesProviderState();
9
9
  const colorScheme = useColorScheme();
10
10
  const styles = useStyles();
11
11
  return (React.createElement(InputAccessoriesContext.Provider, { value: {
@@ -19,8 +19,6 @@ export function InputAccessoriesProvider({ children, }) {
19
19
  onFocusNext,
20
20
  onFocusPrevious,
21
21
  setFocusedInput,
22
- isScrolling,
23
- setIsScrolling,
24
22
  } },
25
23
  children,
26
24
  Platform.OS === "ios" && (React.createElement(InputAccessoryView, { nativeID: inputAccessoryID },
@@ -51,7 +49,6 @@ function useInputAccessoriesProviderState() {
51
49
  const [canFocusNext, setCanFocusNext] = useState(false);
52
50
  const [canFocusPrevious, setCanFocusPrevious] = useState(false);
53
51
  const [elements, setElements] = useState({});
54
- const [isScrolling, setIsScrolling] = useState(false);
55
52
  const keys = Object.keys(elements);
56
53
  const selectedIndex = keys.findIndex(key => key === focusedInput);
57
54
  const nextKey = keys[selectedIndex + 1];
@@ -71,7 +68,5 @@ function useInputAccessoriesProviderState() {
71
68
  setCanFocusPrevious,
72
69
  elements,
73
70
  setElements,
74
- isScrolling,
75
- setIsScrolling,
76
71
  };
77
72
  }