@jobber/components-native 0.99.0 → 0.100.1
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 +4 -6
- package/dist/src/Button/Button.js +2 -2
- package/dist/src/ContentOverlay/BottomSheetKeyboardAwareScrollView.js +19 -0
- package/dist/src/ContentOverlay/ContentOverlay.js +143 -107
- package/dist/src/ContentOverlay/ContentOverlay.style.js +8 -12
- package/dist/src/ContentOverlay/computeContentOverlayBehavior.js +76 -0
- package/dist/src/ContentOverlay/constants.js +1 -0
- package/dist/src/ContentOverlay/hooks/useBottomSheetModalBackHandler.js +25 -0
- package/dist/src/ContentOverlay/index.js +1 -1
- package/dist/src/InputText/InputText.js +44 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/src/ActionLabel/ActionLabel.d.ts +1 -1
- package/dist/types/src/ContentOverlay/BottomSheetKeyboardAwareScrollView.d.ts +11 -0
- package/dist/types/src/ContentOverlay/ContentOverlay.d.ts +2 -5
- package/dist/types/src/ContentOverlay/ContentOverlay.style.d.ts +11 -10
- package/dist/types/src/ContentOverlay/computeContentOverlayBehavior.d.ts +32 -0
- package/dist/types/src/ContentOverlay/constants.d.ts +1 -0
- package/dist/types/src/ContentOverlay/hooks/useBottomSheetModalBackHandler.d.ts +7 -0
- package/dist/types/src/ContentOverlay/index.d.ts +1 -1
- package/dist/types/src/ContentOverlay/types.d.ts +5 -12
- package/jestSetup.js +2 -0
- package/package.json +4 -6
- package/src/ActionLabel/ActionLabel.test.tsx +13 -1
- package/src/ActionLabel/ActionLabel.tsx +6 -1
- package/src/Button/Button.tsx +2 -2
- package/src/ContentOverlay/BottomSheetKeyboardAwareScrollView.tsx +36 -0
- package/src/ContentOverlay/ContentOverlay.stories.tsx +32 -36
- package/src/ContentOverlay/ContentOverlay.style.ts +12 -12
- package/src/ContentOverlay/ContentOverlay.test.tsx +157 -79
- package/src/ContentOverlay/ContentOverlay.tsx +247 -205
- package/src/ContentOverlay/computeContentOverlayBehavior.test.ts +276 -0
- package/src/ContentOverlay/computeContentOverlayBehavior.ts +119 -0
- package/src/ContentOverlay/constants.ts +1 -0
- package/src/ContentOverlay/hooks/useBottomSheetModalBackHandler.test.ts +81 -0
- package/src/ContentOverlay/hooks/useBottomSheetModalBackHandler.ts +36 -0
- package/src/ContentOverlay/index.ts +4 -1
- package/src/ContentOverlay/types.ts +5 -13
- package/src/InputText/InputText.test.tsx +122 -0
- package/src/InputText/InputText.tsx +62 -2
- package/dist/src/ContentOverlay/UNSAFE_WrappedModalize.js +0 -23
- package/dist/types/src/ContentOverlay/UNSAFE_WrappedModalize.d.ts +0 -3
- package/src/ContentOverlay/UNSAFE_WrappedModalize.tsx +0 -41
|
@@ -14,7 +14,8 @@ import type {
|
|
|
14
14
|
TextInputProps,
|
|
15
15
|
TextStyle,
|
|
16
16
|
} from "react-native";
|
|
17
|
-
import { Platform, TextInput } from "react-native";
|
|
17
|
+
import { Platform, TextInput, findNodeHandle } from "react-native";
|
|
18
|
+
import { useBottomSheetInternal } from "@gorhom/bottom-sheet";
|
|
18
19
|
import type { RegisterOptions } from "react-hook-form";
|
|
19
20
|
import type { IconNames } from "@jobber/design";
|
|
20
21
|
import identity from "lodash/identity";
|
|
@@ -22,6 +23,7 @@ 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";
|
|
26
|
+
import { useIsKeyboardHandledByScrollView } from "../ContentOverlay";
|
|
25
27
|
import { useFormController } from "../hooks";
|
|
26
28
|
import type {
|
|
27
29
|
InputFieldStyleOverride,
|
|
@@ -315,6 +317,20 @@ function InputTextInternal(
|
|
|
315
317
|
disabled,
|
|
316
318
|
});
|
|
317
319
|
|
|
320
|
+
// When inside a scrollable ContentOverlay, keyboard offset is handled by
|
|
321
|
+
// KeyboardAwareScrollView. Registering with the bottom-sheet's keyboard
|
|
322
|
+
// state would cause double-counted spacing, so we skip it.
|
|
323
|
+
const isKeyboardHandledByScrollView = useIsKeyboardHandledByScrollView();
|
|
324
|
+
const bottomSheetContext = useBottomSheetInternal(true);
|
|
325
|
+
const shouldHandleBottomSheetKeyboard =
|
|
326
|
+
bottomSheetContext !== null && !isKeyboardHandledByScrollView;
|
|
327
|
+
const animatedKeyboardState = shouldHandleBottomSheetKeyboard
|
|
328
|
+
? bottomSheetContext.animatedKeyboardState
|
|
329
|
+
: undefined;
|
|
330
|
+
const textInputNodesRef = shouldHandleBottomSheetKeyboard
|
|
331
|
+
? bottomSheetContext.textInputNodesRef
|
|
332
|
+
: undefined;
|
|
333
|
+
|
|
318
334
|
// Android doesn't have an accessibility label like iOS does. By adding
|
|
319
335
|
// it as a placeholder it readds it like a label. However we don't want to
|
|
320
336
|
// add a placeholder on iOS.
|
|
@@ -439,11 +455,13 @@ function InputTextInternal(
|
|
|
439
455
|
secureTextEntry={secureTextEntry}
|
|
440
456
|
{...androidA11yProps}
|
|
441
457
|
onFocus={event => {
|
|
458
|
+
handleBottomSheetFocus(event);
|
|
442
459
|
_name && setFocusedInput(_name);
|
|
443
460
|
setFocused(true);
|
|
444
461
|
onFocus?.(event);
|
|
445
462
|
}}
|
|
446
463
|
onBlur={event => {
|
|
464
|
+
handleBottomSheetBlur(event);
|
|
447
465
|
_name && setFocusedInput("");
|
|
448
466
|
setFocused(false);
|
|
449
467
|
onBlur?.(event);
|
|
@@ -470,6 +488,48 @@ function InputTextInternal(
|
|
|
470
488
|
updateFormAndState(removedIOSCharValue);
|
|
471
489
|
}
|
|
472
490
|
|
|
491
|
+
function handleBottomSheetFocus(event?: FocusEvent) {
|
|
492
|
+
if (!animatedKeyboardState || !textInputNodesRef || !event?.nativeEvent) {
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
animatedKeyboardState.set(state => ({
|
|
497
|
+
...state,
|
|
498
|
+
target: event.nativeEvent.target,
|
|
499
|
+
}));
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function handleBottomSheetBlur(event?: FocusEvent) {
|
|
503
|
+
if (!animatedKeyboardState || !textInputNodesRef || !event?.nativeEvent) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
const keyboardState = animatedKeyboardState.get();
|
|
507
|
+
const currentlyFocusedInput = TextInput.State.currentlyFocusedInput();
|
|
508
|
+
const currentFocusedInput =
|
|
509
|
+
currentlyFocusedInput !== null
|
|
510
|
+
? findNodeHandle(
|
|
511
|
+
// @ts-expect-error - TextInput.State.currentlyFocusedInput() returns NativeMethods
|
|
512
|
+
// which is not directly assignable to findNodeHandle's expected type,
|
|
513
|
+
// but it works at runtime. This is a known type limitation in React Native.
|
|
514
|
+
currentlyFocusedInput,
|
|
515
|
+
)
|
|
516
|
+
: null;
|
|
517
|
+
|
|
518
|
+
// Only remove the target if it belongs to the current component
|
|
519
|
+
// and if the currently focused input is not in the targets set
|
|
520
|
+
const shouldRemoveCurrentTarget =
|
|
521
|
+
keyboardState.target === event.nativeEvent.target;
|
|
522
|
+
const shouldIgnoreBlurEvent =
|
|
523
|
+
currentFocusedInput && textInputNodesRef.current.has(currentFocusedInput);
|
|
524
|
+
|
|
525
|
+
if (shouldRemoveCurrentTarget && !shouldIgnoreBlurEvent) {
|
|
526
|
+
animatedKeyboardState.set(state => ({
|
|
527
|
+
...state,
|
|
528
|
+
target: undefined,
|
|
529
|
+
}));
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
473
533
|
function handleClear() {
|
|
474
534
|
handleChangeText("");
|
|
475
535
|
}
|
|
@@ -516,7 +576,7 @@ interface UseTextInputRefProps {
|
|
|
516
576
|
}
|
|
517
577
|
|
|
518
578
|
function useTextInputRef({ ref, onClear }: UseTextInputRefProps) {
|
|
519
|
-
const textInputRef = useRef<
|
|
579
|
+
const textInputRef = useRef<TextInput | null>(null);
|
|
520
580
|
|
|
521
581
|
useImperativeHandle(
|
|
522
582
|
ref,
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import React, { forwardRef, useImperativeHandle, useRef, useState, } from "react";
|
|
2
|
-
import { Modalize } from "react-native-modalize";
|
|
3
|
-
export const UNSAFE_WrappedModalize = forwardRef((props, ref) => {
|
|
4
|
-
const innerRef = useRef(null);
|
|
5
|
-
const [openRenderId, setOpenRenderId] = useState(0);
|
|
6
|
-
useImperativeHandle(ref, () => ({
|
|
7
|
-
open(dest) {
|
|
8
|
-
setOpenRenderId(id => id + 1);
|
|
9
|
-
// Open on a fresh tick for additional safety
|
|
10
|
-
requestAnimationFrame(() => {
|
|
11
|
-
var _a;
|
|
12
|
-
(_a = innerRef.current) === null || _a === void 0 ? void 0 : _a.open(dest);
|
|
13
|
-
});
|
|
14
|
-
},
|
|
15
|
-
close(dest) {
|
|
16
|
-
var _a;
|
|
17
|
-
(_a = innerRef.current) === null || _a === void 0 ? void 0 : _a.close(dest);
|
|
18
|
-
},
|
|
19
|
-
}), []);
|
|
20
|
-
// Use a unique key to force a remount, ensuring we get fresh gesture handler nodes within modalize
|
|
21
|
-
return (React.createElement(Modalize, Object.assign({ key: `modalize-${openRenderId}`, ref: innerRef }, props)));
|
|
22
|
-
});
|
|
23
|
-
UNSAFE_WrappedModalize.displayName = "UNSAFE_WrappedModalize";
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { IHandles } from "react-native-modalize/lib/options";
|
|
3
|
-
export declare const UNSAFE_WrappedModalize: React.ForwardRefExoticComponent<Omit<import("react-native-modalize/lib/options").IProps<any> & React.RefAttributes<any>, "ref"> & React.RefAttributes<IHandles | undefined>>;
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
forwardRef,
|
|
3
|
-
useImperativeHandle,
|
|
4
|
-
useRef,
|
|
5
|
-
useState,
|
|
6
|
-
} from "react";
|
|
7
|
-
import { Modalize } from "react-native-modalize";
|
|
8
|
-
import type { IHandles } from "react-native-modalize/lib/options";
|
|
9
|
-
|
|
10
|
-
type Props = React.ComponentProps<typeof Modalize>;
|
|
11
|
-
|
|
12
|
-
export const UNSAFE_WrappedModalize = forwardRef<IHandles | undefined, Props>(
|
|
13
|
-
(props, ref) => {
|
|
14
|
-
const innerRef = useRef<IHandles | null>(null);
|
|
15
|
-
const [openRenderId, setOpenRenderId] = useState(0);
|
|
16
|
-
|
|
17
|
-
useImperativeHandle(
|
|
18
|
-
ref,
|
|
19
|
-
() => ({
|
|
20
|
-
open(dest) {
|
|
21
|
-
setOpenRenderId(id => id + 1);
|
|
22
|
-
// Open on a fresh tick for additional safety
|
|
23
|
-
requestAnimationFrame(() => {
|
|
24
|
-
innerRef.current?.open(dest);
|
|
25
|
-
});
|
|
26
|
-
},
|
|
27
|
-
close(dest) {
|
|
28
|
-
innerRef.current?.close(dest);
|
|
29
|
-
},
|
|
30
|
-
}),
|
|
31
|
-
[],
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
// Use a unique key to force a remount, ensuring we get fresh gesture handler nodes within modalize
|
|
35
|
-
return (
|
|
36
|
-
<Modalize key={`modalize-${openRenderId}`} ref={innerRef} {...props} />
|
|
37
|
-
);
|
|
38
|
-
},
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
UNSAFE_WrappedModalize.displayName = "UNSAFE_WrappedModalize";
|