@jobber/components-native 0.107.3 → 0.108.1-visual-reg-5479aeb.25

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.
@@ -1,4 +1,4 @@
1
1
  import React from "react";
2
2
  import type { ContentOverlayProps } from "./types";
3
3
  export declare function useIsKeyboardHandledByScrollView(): boolean;
4
- export declare function ContentOverlay({ children, title, accessibilityLabel, fullScreen, showDismiss, isDraggable, adjustToContentHeight, keyboardShouldPersistTaps, scrollEnabled, modalBackgroundColor, onClose, onOpen, onBeforeExit, loading, ref, }: ContentOverlayProps): React.JSX.Element;
4
+ export declare function ContentOverlay({ children, title, accessibilityLabel, fullScreen, showDismiss, isDraggable, adjustToContentHeight, keyboardShouldPersistTaps, scrollEnabled, modalBackgroundColor, onClose, onOpen, onBeforeExit, allowDragWithBeforeExit, enablePanDownToClose, enableContentPanningGesture, snapPoints: customSnapPoints, loading, ref, }: ContentOverlayProps): React.JSX.Element;
@@ -3,6 +3,7 @@ export interface ContentOverlayConfig {
3
3
  adjustToContentHeight: boolean;
4
4
  isDraggable: boolean;
5
5
  hasOnBeforeExit: boolean;
6
+ allowDragWithBeforeExit: boolean;
6
7
  showDismiss: boolean;
7
8
  }
8
9
  export interface ContentOverlayState {
@@ -63,6 +63,29 @@ export interface ContentOverlayProps {
63
63
  * Callback that is called between overlay is closed and when the "x" button is pressed
64
64
  */
65
65
  readonly onBeforeExit?: () => void;
66
+ /**
67
+ * Whether panning down on the content area closes the sheet. Set false to prevent the sheet from being closed by panning down.
68
+ * Defaults to the resolved draggable state.
69
+ */
70
+ readonly enablePanDownToClose?: boolean;
71
+ /**
72
+ * When `true`, `onBeforeExit` intercepts the dismiss button and back press
73
+ * but does not disable drag. Drag-to-dismiss bypasses `onBeforeExit`.
74
+ * @default false
75
+ */
76
+ readonly allowDragWithBeforeExit?: boolean;
77
+ /**
78
+ * Whether pan on the content area drags the sheet. Set `false` to let
79
+ * nested scrollables own the gesture. Defaults to the resolved draggable
80
+ * state.
81
+ */
82
+ readonly enableContentPanningGesture?: boolean;
83
+ /**
84
+ * Explicit snap points for the overlay (percentages or pixel values).
85
+ * When provided, `adjustToContentHeight` is ignored and the sheet snaps
86
+ * to these positions instead of the measured content height.
87
+ */
88
+ readonly snapPoints?: (string | number)[];
66
89
  /**
67
90
  * Boolean to show a disabled state
68
91
  * @default false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.107.3",
3
+ "version": "0.108.1-visual-reg-5479aeb.25+5479aebb",
4
4
  "license": "MIT",
5
5
  "description": "React Native implementation of Atlantis",
6
6
  "repository": {
@@ -73,7 +73,7 @@
73
73
  "devDependencies": {
74
74
  "@babel/runtime": "^7.29.2",
75
75
  "@gorhom/bottom-sheet": "^5.2.8",
76
- "@jobber/design": "0.107.0",
76
+ "@jobber/design": "0.107.1-visual-reg-5479aeb.32+5479aebb",
77
77
  "@jobber/hooks": "2.21.0",
78
78
  "@react-native-community/datetimepicker": "^8.4.5",
79
79
  "@react-native/babel-preset": "^0.82.1",
@@ -124,5 +124,5 @@
124
124
  "react-native-screens": ">=4.18.0",
125
125
  "react-native-svg": ">=12.0.0"
126
126
  },
127
- "gitHead": "f56f1035f43410c3b2597e5198adf7fdce8b3453"
127
+ "gitHead": "5479aebba6969f3d68ca0fb11654375c8439ab96"
128
128
  }
@@ -36,7 +36,10 @@ import { useIsScreenReaderEnabled } from "../hooks";
36
36
  import { IconButton } from "../IconButton";
37
37
  import { Heading } from "../Heading";
38
38
  import { useAtlantisI18n } from "../hooks/useAtlantisI18n";
39
- import { useAtlantisTheme } from "../AtlantisThemeContext";
39
+ import {
40
+ AtlantisThemeContextProvider,
41
+ useAtlantisTheme,
42
+ } from "../AtlantisThemeContext";
40
43
 
41
44
  /**
42
45
  * Signals whether keyboard handling inside a ContentOverlay is delegated to
@@ -81,6 +84,10 @@ export function ContentOverlay({
81
84
  onClose,
82
85
  onOpen,
83
86
  onBeforeExit,
87
+ allowDragWithBeforeExit = false,
88
+ enablePanDownToClose,
89
+ enableContentPanningGesture,
90
+ snapPoints: customSnapPoints,
84
91
  loading = false,
85
92
  ref,
86
93
  }: ContentOverlayProps) {
@@ -92,7 +99,7 @@ export function ContentOverlay({
92
99
 
93
100
  const styles = useStyles();
94
101
  const { t } = useAtlantisI18n();
95
- const { tokens } = useAtlantisTheme();
102
+ const { theme, tokens } = useAtlantisTheme();
96
103
  const isScreenReaderEnabled = useIsScreenReaderEnabled();
97
104
 
98
105
  const behavior = computeContentOverlayBehavior(
@@ -101,6 +108,7 @@ export function ContentOverlay({
101
108
  adjustToContentHeight,
102
109
  isDraggable,
103
110
  hasOnBeforeExit: onBeforeExit !== undefined,
111
+ allowDragWithBeforeExit,
104
112
  showDismiss,
105
113
  },
106
114
  {
@@ -124,12 +132,16 @@ export function ContentOverlay({
124
132
 
125
133
  // enableDynamicSizing will add another snap point of the content height
126
134
  const snapPoints = useMemo(() => {
135
+ if (customSnapPoints && customSnapPoints.length > 0) {
136
+ return customSnapPoints;
137
+ }
138
+
127
139
  // There is a bug with "restore" behavior after keyboard is dismissed.
128
140
  // https://github.com/gorhom/react-native-bottom-sheet/issues/2465
129
141
  // providing a 100% snap point "fixes" it for now, but there is an approved PR to fix it
130
142
  // that just needs to be merged and released: https://github.com/gorhom/react-native-bottom-sheet/pull/2511
131
143
  return ["100%"];
132
- }, []);
144
+ }, [customSnapPoints]);
133
145
 
134
146
  const onCloseController = () => {
135
147
  if (!onBeforeExit) {
@@ -296,43 +308,55 @@ export function ContentOverlay({
296
308
  handleIndicatorStyle={handleIndicatorStyles}
297
309
  backdropComponent={backdropComponent}
298
310
  snapPoints={snapPoints}
299
- enablePanDownToClose={effectiveIsDraggable}
300
- enableContentPanningGesture={effectiveIsDraggable}
311
+ enablePanDownToClose={enablePanDownToClose ?? effectiveIsDraggable}
312
+ enableContentPanningGesture={
313
+ enableContentPanningGesture ?? effectiveIsDraggable
314
+ }
301
315
  enableHandlePanningGesture={effectiveIsDraggable}
302
- enableDynamicSizing={behavior.initialHeight === "contentHeight"}
316
+ enableDynamicSizing={
317
+ !customSnapPoints && behavior.initialHeight === "contentHeight"
318
+ }
303
319
  keyboardBehavior="interactive"
304
320
  keyboardBlurBehavior="restore"
305
321
  topInset={topInset}
306
322
  onDismiss={() => onClose?.()}
307
323
  >
308
- <ContentOverlayKeyboardContext.Provider value={scrollEnabled}>
309
- {scrollEnabled ? (
310
- <BottomSheetKeyboardAwareScrollView
311
- ref={scrollViewRef}
312
- contentContainerStyle={{ paddingBottom: insets.bottom }}
313
- keyboardShouldPersistTaps={
314
- keyboardShouldPersistTaps ? "handled" : "never"
315
- }
316
- showsVerticalScrollIndicator={false}
317
- onScroll={handleOnScroll}
318
- stickyHeaderIndices={[0]}
319
- bottomOffset={KEYBOARD_TOP_PADDING_AUTO_SCROLL}
320
- >
321
- {renderHeader()}
322
- <View testID="ATL-Overlay-Children">{children}</View>
323
- </BottomSheetKeyboardAwareScrollView>
324
- ) : (
325
- <BottomSheetView>
326
- {renderHeader()}
327
- <View
328
- style={{ paddingBottom: insets.bottom }}
329
- testID="ATL-Overlay-Children"
324
+ {/*
325
+ BottomSheetModal renders its children through a portal mounted outside
326
+ this component's subtree, so the AtlantisThemeContext is lost. Re-apply
327
+ the active theme here so descendants (e.g. the header title) resolve the
328
+ correct themed tokens.
329
+ */}
330
+ <AtlantisThemeContextProvider dangerouslyOverrideTheme={theme}>
331
+ <ContentOverlayKeyboardContext.Provider value={scrollEnabled}>
332
+ {scrollEnabled ? (
333
+ <BottomSheetKeyboardAwareScrollView
334
+ ref={scrollViewRef}
335
+ contentContainerStyle={{ paddingBottom: insets.bottom }}
336
+ keyboardShouldPersistTaps={
337
+ keyboardShouldPersistTaps ? "handled" : "never"
338
+ }
339
+ showsVerticalScrollIndicator={false}
340
+ onScroll={handleOnScroll}
341
+ stickyHeaderIndices={[0]}
342
+ bottomOffset={KEYBOARD_TOP_PADDING_AUTO_SCROLL}
330
343
  >
331
- {children}
332
- </View>
333
- </BottomSheetView>
334
- )}
335
- </ContentOverlayKeyboardContext.Provider>
344
+ {renderHeader()}
345
+ <View testID="ATL-Overlay-Children">{children}</View>
346
+ </BottomSheetKeyboardAwareScrollView>
347
+ ) : (
348
+ <BottomSheetView>
349
+ {renderHeader()}
350
+ <View
351
+ style={{ paddingBottom: insets.bottom }}
352
+ testID="ATL-Overlay-Children"
353
+ >
354
+ {children}
355
+ </View>
356
+ </BottomSheetView>
357
+ )}
358
+ </ContentOverlayKeyboardContext.Provider>
359
+ </AtlantisThemeContextProvider>
336
360
  </BottomSheetModal>
337
361
  );
338
362
  }
@@ -11,6 +11,7 @@ const defaultConfig: ContentOverlayConfig = {
11
11
  adjustToContentHeight: false,
12
12
  isDraggable: true,
13
13
  hasOnBeforeExit: false,
14
+ allowDragWithBeforeExit: false,
14
15
  showDismiss: false,
15
16
  };
16
17
 
@@ -112,6 +113,45 @@ describe("computeContentOverlayBehavior", () => {
112
113
 
113
114
  expect(result.isDraggable).toBe(false);
114
115
  });
116
+
117
+ it("respects isDraggable=true when onBeforeExit is present and allowDragWithBeforeExit=true", () => {
118
+ const config = aConfig({
119
+ isDraggable: true,
120
+ hasOnBeforeExit: true,
121
+ allowDragWithBeforeExit: true,
122
+ });
123
+ const state = aState();
124
+
125
+ const result = computeContentOverlayBehavior(config, state);
126
+
127
+ expect(result.isDraggable).toBe(true);
128
+ });
129
+
130
+ it("respects isDraggable=false when onBeforeExit is present and allowDragWithBeforeExit=true", () => {
131
+ const config = aConfig({
132
+ isDraggable: false,
133
+ hasOnBeforeExit: true,
134
+ allowDragWithBeforeExit: true,
135
+ });
136
+ const state = aState();
137
+
138
+ const result = computeContentOverlayBehavior(config, state);
139
+
140
+ expect(result.isDraggable).toBe(false);
141
+ });
142
+
143
+ it("still overrides to false when onBeforeExit is present and allowDragWithBeforeExit=false (default)", () => {
144
+ const config = aConfig({
145
+ isDraggable: true,
146
+ hasOnBeforeExit: true,
147
+ allowDragWithBeforeExit: false,
148
+ });
149
+ const state = aState();
150
+
151
+ const result = computeContentOverlayBehavior(config, state);
152
+
153
+ expect(result.isDraggable).toBe(false);
154
+ });
115
155
  });
116
156
 
117
157
  describe("showDismiss", () => {
@@ -3,6 +3,7 @@ export interface ContentOverlayConfig {
3
3
  adjustToContentHeight: boolean;
4
4
  isDraggable: boolean;
5
5
  hasOnBeforeExit: boolean;
6
+ allowDragWithBeforeExit: boolean;
6
7
  showDismiss: boolean;
7
8
  }
8
9
 
@@ -76,14 +77,17 @@ function computeInitialHeight(
76
77
 
77
78
  /**
78
79
  * Draggability determination:
79
- * - hasOnBeforeExit: true → false (silent override, regardless of isDraggable prop)
80
+ * - hasOnBeforeExit: true && !allowDragWithBeforeExit → false (legacy override)
81
+ * - hasOnBeforeExit: true && allowDragWithBeforeExit → use isDraggable prop value
80
82
  * - Otherwise → use isDraggable prop value
81
83
  *
82
- * This silent override exists because onBeforeExit needs to intercept close attempts,
83
- * and dragging would bypass that interception.
84
+ * The legacy override exists because onBeforeExit needs to intercept close
85
+ * attempts, and dragging would bypass that interception. Consumers that
86
+ * explicitly accept the asymmetry (e.g. button-press confirmation,
87
+ * drag-dismiss silent) can opt out via `allowDragWithBeforeExit`.
84
88
  */
85
89
  function computeIsDraggable(config: ContentOverlayConfig): boolean {
86
- if (config.hasOnBeforeExit) {
90
+ if (config.hasOnBeforeExit && !config.allowDragWithBeforeExit) {
87
91
  return false;
88
92
  }
89
93
 
@@ -66,6 +66,34 @@ export interface ContentOverlayProps {
66
66
  */
67
67
  readonly onBeforeExit?: () => void;
68
68
 
69
+ /**
70
+ * Whether panning down on the content area closes the sheet. Set false to prevent the sheet from being closed by panning down.
71
+ * Defaults to the resolved draggable state.
72
+ */
73
+
74
+ readonly enablePanDownToClose?: boolean;
75
+
76
+ /**
77
+ * When `true`, `onBeforeExit` intercepts the dismiss button and back press
78
+ * but does not disable drag. Drag-to-dismiss bypasses `onBeforeExit`.
79
+ * @default false
80
+ */
81
+ readonly allowDragWithBeforeExit?: boolean;
82
+
83
+ /**
84
+ * Whether pan on the content area drags the sheet. Set `false` to let
85
+ * nested scrollables own the gesture. Defaults to the resolved draggable
86
+ * state.
87
+ */
88
+ readonly enableContentPanningGesture?: boolean;
89
+
90
+ /**
91
+ * Explicit snap points for the overlay (percentages or pixel values).
92
+ * When provided, `adjustToContentHeight` is ignored and the sheet snaps
93
+ * to these positions instead of the measured content height.
94
+ */
95
+ readonly snapPoints?: (string | number)[];
96
+
69
97
  /**
70
98
  * Boolean to show a disabled state
71
99
  * @default false