react-native-snap-sheet 1.0.2 → 1.0.3

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/index.ts CHANGED
@@ -104,6 +104,15 @@ export interface SnapSheetProps extends SnapSheetBaseProps {
104
104
  */
105
105
  initialSnapIndex?: number;
106
106
 
107
+ /**
108
+ * The lowest snap point the sheet can attain.
109
+ *
110
+ * This is useful in preventing the sheet from completing closing
111
+ *
112
+ * @default 0
113
+ */
114
+ minSnapIndex?: number;
115
+
107
116
  /**
108
117
  * Disable user interactions on the snap sheet
109
118
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-snap-sheet",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "",
5
5
  "homepage": "https://github.com/deflexable/react-native-snap-sheet#readme",
6
6
  "bugs": {
@@ -19,7 +19,7 @@
19
19
  "test": "echo \"Error: no test specified\" && exit 1"
20
20
  },
21
21
  "dependencies": {
22
- "react-native-dodge-keyboard": "^1.0.2",
22
+ "react-native-dodge-keyboard": "^1.0.3",
23
23
  "react-native-push-back": "^1.0.0"
24
24
  }
25
- }
25
+ }
package/src/snapsheet.js CHANGED
@@ -1,14 +1,16 @@
1
- import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
2
- import { Animated, Keyboard, PanResponder, StyleSheet, useAnimatedValue, View } from "react-native";
3
- import DodgeKeyboard, { ReactHijacker } from "react-native-dodge-keyboard";
4
- import { doRendable, isNumber } from "./utils";
1
+ import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState, cloneElement } from "react";
2
+ import { Animated, PanResponder, StyleSheet, useAnimatedValue, View } from "react-native";
3
+ import DodgeKeyboard, { createHijackedElement, ReactHijacker, __HijackNode } from "react-native-dodge-keyboard";
4
+ import { doRendable, isNumber, isPositiveNumber } from "./utils";
5
5
  import { styling } from "./styling";
6
6
 
7
7
  const PixelRate = 70 / 100; // 70ms to 100 pixels
8
+ const CheckFocusedNode = '__fakeSnapSheetFocused';
8
9
 
9
10
  const SnapSheet = forwardRef(function SnapSheet({
10
11
  snapPoints = [],
11
12
  initialSnapIndex = 0,
13
+ minSnapIndex = 0,
12
14
  onSnapIndex,
13
15
  onSnapFinish,
14
16
  snapWhileDecelerating = false,
@@ -17,57 +19,79 @@ const SnapSheet = forwardRef(function SnapSheet({
17
19
  inheritScrollVelocityOnCollapse,
18
20
  renderHandle,
19
21
  handleColor,
20
- keyboardDodgingBehaviour = 'optimum', // off, whole
22
+ keyboardDodgingBehaviour = 'optimum',
21
23
  keyboardDodgingOffset = 10,
22
24
  children,
23
25
  disabled,
24
26
  currentAnchorId,
27
+ __checkIfElementIsFocused,
28
+ __loosenMinSnap,
25
29
  __shaky_sheet
26
30
  }, ref) {
27
31
  const isLift = keyboardDodgingBehaviour === 'whole';
28
- const isOptimum = keyboardDodgingBehaviour === 'optimum';
29
32
 
30
33
  if (!['optimum', 'whole', 'off'].includes(keyboardDodgingBehaviour))
31
34
  throw `keyboardDodgingBehaviour must be any of ${['optimum', 'whole', 'off']} but got ${keyboardDodgingBehaviour}`;
32
35
 
33
- useMemo(() => {
34
- if (snapPoints.length < 2) throw new Error('snapPoints must have at least two items');
35
- snapPoints.forEach((v, i, a) => {
36
- if (typeof v !== 'number' || !isNumber(v))
37
- throw new Error(`snapPoints must have a valid number but got ${v} at position ${i}`);
38
- if (i !== a.length - 1 && v >= a[i + 1])
39
- throw new Error(`snapPoints must be in accending order but got ${v} before ${a[i + 1]}`);
40
- });
41
- if (!Number.isInteger(initialSnapIndex) || initialSnapIndex < 0)
42
- throw new Error(`initialSnapIndex should be a positive integer but got:${initialSnapIndex}`);
43
- if (initialSnapIndex >= snapPoints.length) throw new Error(`initialSnapIndex is out of range`);
44
- }, snapPoints);
45
- const initSnapPoints = snapPoints;
36
+ if (snapPoints.length < 2) throw new Error('snapPoints must have at least two items');
37
+ snapPoints.forEach((v, i, a) => {
38
+ if (typeof v !== 'number' || !isNumber(v))
39
+ throw new Error(`snapPoints must have a valid number but got ${v} at position ${i}`);
40
+ if (i !== a.length - 1 && v >= a[i + 1])
41
+ throw new Error(`snapPoints must be in accending order but got ${v} before ${a[i + 1]}`);
42
+ });
43
+ if (!Number.isInteger(initialSnapIndex) || initialSnapIndex < 0)
44
+ throw new Error(`initialSnapIndex should be a positive integer but got:${initialSnapIndex}`);
45
+ if (initialSnapIndex >= snapPoints.length) throw new Error(`initialSnapIndex is out of range`);
46
+
47
+ if (!Number.isInteger(minSnapIndex) || minSnapIndex < 0)
48
+ throw new Error(`minSnapIndex should be a positive integer but got:${minSnapIndex}`);
49
+
50
+ if (minSnapIndex >= snapPoints.length) throw new Error(`minSnapIndex is out of range`);
51
+ initialSnapIndex = Math.max(initialSnapIndex, minSnapIndex);
52
+
53
+ if (__checkIfElementIsFocused !== undefined && typeof __checkIfElementIsFocused !== 'function')
54
+ throw `expected '__checkIfElementIsFocused' to be a function but got ${__checkIfElementIsFocused}`;
55
+
56
+ if (isLift) __checkIfElementIsFocused = r => !!r?.[CheckFocusedNode];
57
+
58
+ const flattenStyle = StyleSheet.flatten(style) || {};
46
59
 
47
60
  const [scrollEnabled, setScrollEnabled] = useState(false);
48
61
  const [dodgeOffset, setDodgeOffset] = useState(0);
49
- const [requiredLift, setRequiredLift] = useState(0);
50
62
  const [currentIndex, setCurrentIndex] = useState(initialSnapIndex);
51
63
  const [finishedIndex, setFinishedIndex] = useState(initialSnapIndex);
52
64
  const [prefferedAnchor, setPrefferedAnchor] = useState();
53
65
 
54
- const extraLift = dodgeOffset && ((isOptimum ? dodgeOffset : isLift ? requiredLift : 0) || 0);
66
+ snapPoints = snapPoints.map(v => v + dodgeOffset);
67
+ const snapPointsKey = `${snapPoints}`;
55
68
 
56
- snapPoints = snapPoints.map(v => v + extraLift);
69
+ const fixHeight =
70
+ (isPositiveNumber(flattenStyle.minHeight) && isPositiveNumber(flattenStyle.height))
71
+ ? Math.max(flattenStyle.minHeight, flattenStyle.height)
72
+ : flattenStyle.height;
73
+ const fixMaxHeight = flattenStyle.maxHeight;
74
+
75
+ const PotentialHeight =
76
+ (isPositiveNumber(fixHeight) && isPositiveNumber(fixMaxHeight))
77
+ ? Math.min(fixHeight, fixMaxHeight)
78
+ : fixHeight;
57
79
  // console.log('sheetLifing:', { extraLift, dodgeOffset, requiredLift, initSnapPoints: `${initSnapPoints}`, snapPoints: `${snapPoints}` });
58
- const MAX_HEIGHT = snapPoints.slice(-1)[0];
80
+ const MODAL_HEIGHT =
81
+ isPositiveNumber(PotentialHeight)
82
+ ? PotentialHeight
83
+ : snapPoints.slice(-1)[0] - snapPoints[0];
59
84
 
60
- const snapTranslateValues = useMemo(() => snapPoints.map(h => MAX_HEIGHT - h), snapPoints);
85
+ const snapTranslateValues = useMemo(() => snapPoints.map(h => h - MODAL_HEIGHT), [snapPointsKey]);
61
86
 
62
- const translateY = useAnimatedValue(snapTranslateValues[initialSnapIndex]);
87
+ const bottomY = useAnimatedValue(snapTranslateValues[initialSnapIndex]);
63
88
 
64
89
  /**
65
90
  * @type {import("react").RefObject<{[key: string]: { ref: import('react-native').ScrollView, scrollY: 0, location: number[], anchorId: boolean }}>}
66
91
  */
67
92
  const scrollRefObj = useRef({});
68
- const lastOffset = useRef(translateY._value);
93
+ const lastOffset = useRef(bottomY._value);
69
94
  const lastSnapIndex = useRef(initialSnapIndex);
70
- const bottomFakePlaceholderRef = useRef();
71
95
  const instantPrefferAnchor = useRef();
72
96
  instantPrefferAnchor.current = prefferedAnchor;
73
97
 
@@ -77,42 +101,30 @@ const SnapSheet = forwardRef(function SnapSheet({
77
101
  const instantScrollEnabled = useRef(scrollEnabled);
78
102
  instantScrollEnabled.current = scrollEnabled;
79
103
 
80
- const updateKeyboardOffset = () => {
81
- if (!isLift) {
82
- setRequiredLift(0);
83
- return;
84
- }
85
- const keyboardInfo = Keyboard.metrics();
86
- if (keyboardInfo?.height && keyboardInfo.screenY) {
87
- bottomFakePlaceholderRef.current.measureInWindow((x, y) => {
88
- const remains = y - keyboardInfo.screenY;
89
- setRequiredLift(Math.max(0, remains));
90
- });
91
- } else setRequiredLift(0);
92
- }
93
-
94
- useEffect(updateKeyboardOffset, [dodgeOffset, ...initSnapPoints]);
95
-
96
104
  const getCurrentSnap = (draggingUpward) => {
97
- const shownHeight = MAX_HEIGHT - translateY._value;
105
+ const shownHeight = bottomY._value + MODAL_HEIGHT;
98
106
  const currentSnapIndex = draggingUpward ? snapPoints.findIndex((v, i, a) => v <= shownHeight && (i === a.length - 1 || shownHeight < a[i + 1]))
99
107
  : snapPoints.findIndex((v, i, a) => v >= shownHeight && (!i || shownHeight > a[i - 1]));
100
108
 
101
109
  return currentSnapIndex;
102
110
  }
103
111
 
104
- const snapToIndex = (index, force, velocity, onFinish) => {
112
+ const snapToIndex = useRef();
113
+
114
+ snapToIndex.current = (index, force, velocity, onFinish) => {
105
115
  if (disabled && !force) return;
106
116
 
107
117
  if (!Number.isInteger(index) || index < 0 || index > snapPoints.length - 1)
108
118
  throw new Error(`invalid snap index:${index}, index must be within range 0 - ${snapPoints.length - 1}`);
109
119
 
120
+ if (index < minSnapIndex) index = minSnapIndex;
121
+
110
122
  const newY = snapTranslateValues[index];
111
123
 
112
124
  if (__shaky_sheet && lastOffset.current !== newY)
113
- translateY.setValue(lastOffset.current);
125
+ bottomY.setValue(lastOffset.current);
114
126
 
115
- const prevY = translateY._value;
127
+ const prevY = bottomY._value;
116
128
  setScrollEnabled(index === snapPoints.length - 1);
117
129
  setCurrentIndex(index);
118
130
 
@@ -131,7 +143,7 @@ const SnapSheet = forwardRef(function SnapSheet({
131
143
 
132
144
  // console.log('snapTimer:', { timeout, pixel });
133
145
 
134
- Animated.spring(translateY, {
146
+ Animated.spring(bottomY, {
135
147
  velocity,
136
148
  toValue: newY,
137
149
  useNativeDriver: true
@@ -148,13 +160,13 @@ const SnapSheet = forwardRef(function SnapSheet({
148
160
 
149
161
  useImperativeHandle(ref, () => ({
150
162
  snap: index => {
151
- snapToIndex(index, true);
163
+ snapToIndex.current(index, true);
152
164
  }
153
- }), snapPoints);
165
+ }));
154
166
 
155
167
  useEffect(() => {
156
- snapToIndex(Math.min(lastSnapIndex.current, snapPoints.length - 1), true);
157
- }, snapPoints);
168
+ snapToIndex.current(Math.min(lastSnapIndex.current, snapPoints.length - 1), true);
169
+ }, [snapPointsKey]);
158
170
 
159
171
  const panResponder = useMemo(() => {
160
172
 
@@ -178,10 +190,12 @@ const SnapSheet = forwardRef(function SnapSheet({
178
190
  onPanResponderMove: (_, gesture) => {
179
191
  const newY = gesture.dy + lastOffset.current;
180
192
 
181
- if (newY < 0) return; // prevent overscrolling upward
182
- if (newY > MAX_HEIGHT) return;
193
+ if (
194
+ newY < snapTranslateValues[__loosenMinSnap ? 0 : minSnapIndex] ||
195
+ newY > snapTranslateValues.slice(-1)[0]
196
+ ) return;
183
197
 
184
- translateY.setValue(newY);
198
+ bottomY.setValue(newY);
185
199
  },
186
200
  onPanResponderRelease: (_, gesture) => {
187
201
  const { dy, vy } = gesture; // when vy is lesser, it is scroll up
@@ -196,7 +210,7 @@ const SnapSheet = forwardRef(function SnapSheet({
196
210
  vy > 0.3 ? 0 : currentSnapIndex;
197
211
  const willFullyShow = newSnapIndex === snapPoints.length - 1;
198
212
 
199
- snapToIndex(newSnapIndex, true, draggingUpward ? vy : undefined);
213
+ snapToIndex.current(newSnapIndex, true, draggingUpward ? vy : undefined);
200
214
 
201
215
  // Only scroll if there was a fling velocity upward
202
216
  if (inheritScrollVelocityOnExpand && willFullyShow && vy < -0.1) {
@@ -214,22 +228,24 @@ const SnapSheet = forwardRef(function SnapSheet({
214
228
  }
215
229
  }
216
230
  });
217
- }, [!disabled, ...snapPoints]);
218
-
219
- const conStyle = useMemo(() => ({
220
- position: "absolute",
221
- width: "100%",
222
- backgroundColor: "#fff",
223
- borderTopLeftRadius: 25,
224
- borderTopRightRadius: 25,
225
- zIndex: 1,
226
- ...StyleSheet.flatten(style),
227
- bottom: 0,
228
- height: MAX_HEIGHT,
229
- transform: [{ translateY }]
230
- }), [MAX_HEIGHT, style]);
231
-
232
- useEffect(() => updatePrefferAnchor, [currentAnchorId]);
231
+ }, [!disabled, snapPointsKey, minSnapIndex]);
232
+
233
+ const conStyle = useMemo(() => {
234
+ const { height, minHeight, maxHeight, ...rest } = flattenStyle;
235
+
236
+ return {
237
+ position: "absolute",
238
+ width: "100%",
239
+ backgroundColor: "#fff",
240
+ borderTopLeftRadius: 25,
241
+ borderTopRightRadius: 25,
242
+ zIndex: 1,
243
+ height: (Object.hasOwn(flattenStyle, 'height') && height === undefined) ? undefined : MODAL_HEIGHT,
244
+ ...rest,
245
+ bottom: bottomY
246
+ };
247
+ }, [snapPointsKey, style]);
248
+
233
249
  const updateAnchorReducer = useRef();
234
250
 
235
251
  const scheduleAnchorUpdate = (timeout = 100) => {
@@ -242,6 +258,7 @@ const SnapSheet = forwardRef(function SnapSheet({
242
258
  const directAnchor = rankedAnchors.find(v => v[1].anchorId === currentAnchorId);
243
259
  setPrefferedAnchor(directAnchor?.[0]);
244
260
  }
261
+ useEffect(updatePrefferAnchor, [currentAnchorId]);
245
262
 
246
263
  const onAnchorScroll = (e, instanceId) => {
247
264
  const scrollY = e.nativeEvent.contentOffset.y;
@@ -273,7 +290,7 @@ const SnapSheet = forwardRef(function SnapSheet({
273
290
 
274
291
  const currentSnapIndex = getCurrentSnap(false);
275
292
  const newSnapIndex = snapWhileDecelerating ? Math.max(0, currentSnapIndex - 1) : 0;
276
- snapToIndex(newSnapIndex, false, -scrollVelocity);
293
+ snapToIndex.current(newSnapIndex, false, -scrollVelocity);
277
294
  }
278
295
  }
279
296
 
@@ -285,6 +302,8 @@ const SnapSheet = forwardRef(function SnapSheet({
285
302
  const disableDodging = keyboardDodgingBehaviour === 'off';
286
303
  const sameIndex = currentIndex === finishedIndex;
287
304
 
305
+ const instanceIdIterator = useRef(0);
306
+
288
307
  return (
289
308
  <View style={styling.absoluteFill}>
290
309
  <Animated.View
@@ -300,16 +319,19 @@ const SnapSheet = forwardRef(function SnapSheet({
300
319
  <DodgeKeyboard
301
320
  offset={keyboardDodgingOffset}
302
321
  disabled={!sameIndex || disableDodging}
322
+ checkIfElementIsFocused={__checkIfElementIsFocused}
303
323
  onHandleDodging={({ liftUp }) => {
304
324
  setDodgeOffset(liftUp);
305
325
  }}>
306
326
  {ReactHijacker({
307
327
  children,
328
+ enableLocator: true,
308
329
  doHijack: (node, path) => {
309
- if (node?.props?.['snap_sheet_scan_off']) return { element: node };
330
+ if (node?.props?.['snap_sheet_scan_off'] || node?.props?.__checking_snap_scrollable) return;
331
+ if (!isScrollable(node)) return;
310
332
 
311
- if (isScrollable(node)) {
312
- const instanceId = path.join(',');
333
+ const renderer = () => {
334
+ const instanceId = useMemo(() => `${++instanceIdIterator.current}`, []);
313
335
 
314
336
  const initNode = () => {
315
337
  if (!scrollRefObj.current[instanceId])
@@ -323,43 +345,55 @@ const SnapSheet = forwardRef(function SnapSheet({
323
345
  }
324
346
  initNode();
325
347
 
326
- return {
327
- props: {
328
- ...node?.props,
329
- ...disableDodging ? {} : { ['dodge_keyboard_scrollable']: true },
330
- ref: r => {
331
- if (r) {
332
- initNode();
333
- // if (scrollRefObj.current[instanceId].ref !== r) scheduleAnchorUpdate();
334
- scrollRefObj.current[instanceId].ref = r;
335
- } else if (scrollRefObj.current[instanceId]) {
336
- delete scrollRefObj.current[instanceId];
337
- scheduleAnchorUpdate();
338
- }
339
-
340
- const thatRef = node.props?.ref;
341
- if (typeof thatRef === 'function') {
342
- thatRef(r);
343
- } else if (thatRef) thatRef.current = r;
344
- },
345
- ...prefferedAnchor === instanceId ? { scrollEnabled } : {},
346
- onScroll: (e) => {
347
- onAnchorScroll(e, instanceId);
348
- return node.props?.onScroll?.(e);
348
+ const newProps = {
349
+ ...node?.props,
350
+ __checking_snap_scrollable: true,
351
+ ...disableDodging ? {} : { ['dodge_keyboard_scrollable']: true },
352
+ ref: r => {
353
+ if (r) {
354
+ initNode();
355
+ // if (scrollRefObj.current[instanceId].ref !== r) scheduleAnchorUpdate();
356
+ scrollRefObj.current[instanceId].ref = r;
357
+ } else if (scrollRefObj.current[instanceId]) {
358
+ delete scrollRefObj.current[instanceId];
359
+ scheduleAnchorUpdate();
349
360
  }
361
+
362
+ const thatRef = node.props?.ref;
363
+ if (typeof thatRef === 'function') {
364
+ thatRef(r);
365
+ } else if (thatRef) thatRef.current = r;
366
+ },
367
+ ...prefferedAnchor === instanceId ? { scrollEnabled } : {},
368
+ onScroll: (e) => {
369
+ onAnchorScroll(e, instanceId);
370
+ return node.props?.onScroll?.(e);
350
371
  }
351
- }
372
+ };
373
+
374
+ return cloneElement(node, newProps);
352
375
  }
376
+
377
+ return createHijackedElement(
378
+ <__HijackNode>
379
+ {renderer}
380
+ </__HijackNode>
381
+ );
353
382
  }
354
383
  })}
384
+ {isLift ?
385
+ <View
386
+ ref={r => {
387
+ if (r) {
388
+ r[CheckFocusedNode] = true;
389
+ }
390
+ }}
391
+ dodge_keyboard_input
392
+ style={styling.fakePlaceholder}
393
+ /> : null}
355
394
  </DodgeKeyboard>
356
395
  </View>
357
396
  </Animated.View>
358
- {isLift ?
359
- <View
360
- ref={bottomFakePlaceholderRef}
361
- style={styling.fakePlaceholder}
362
- onLayout={updateKeyboardOffset} /> : null}
363
397
  </View>
364
398
  );
365
399
  });
@@ -1,6 +1,6 @@
1
- import { forwardRef, useContext, useEffect, useImperativeHandle, useMemo, useReducer, useRef, useState } from "react";
2
- import { Pressable, StyleSheet, View } from "react-native";
3
- import { isDodgeInput, ReactHijacker } from "react-native-dodge-keyboard";
1
+ import { forwardRef, useContext, useEffect, useImperativeHandle, useMemo, useReducer, useRef, useState, cloneElement } from "react";
2
+ import { Pressable, StyleSheet, View, PixelRatio } from "react-native";
3
+ import { createHijackedElement, isDodgeInput, ReactHijacker, __HijackNode } from "react-native-dodge-keyboard";
4
4
  import { useBackButton } from "react-native-push-back";
5
5
  import { doRendable, isNumber } from "./utils";
6
6
  import { PortalContext } from "./provider";
@@ -8,7 +8,8 @@ import { styling } from "./styling";
8
8
  import SnapSheet from "./snapsheet";
9
9
 
10
10
  const ModalState = ['closed', 'middle', 'opened'];
11
- const CenteredSheetStyle = { width: 0 };
11
+ const CenteredSheetStyle = { width: 0, height: undefined };
12
+ const CheckFocusedNode = '__fakeSnapSheetModalFocused';
12
13
 
13
14
  export const SnapSheetModalBase = forwardRef(function SnapSheetModalBase({
14
15
  onOpened,
@@ -29,6 +30,8 @@ export const SnapSheetModalBase = forwardRef(function SnapSheetModalBase({
29
30
  children,
30
31
  ...restProps
31
32
  }, ref) {
33
+ const isLift = restProps?.keyboardDodgingBehaviour === 'whole';
34
+
32
35
  centered = !!centered;
33
36
  useMemo(() => {
34
37
  if (centered) {
@@ -90,7 +93,7 @@ export const SnapSheetModalBase = forwardRef(function SnapSheetModalBase({
90
93
  const snapPoints = useMemo(() => {
91
94
  if (centered) {
92
95
  if (sizingReady)
93
- return [-(contentHeight / 2), viewHeight / 2];
96
+ return [0, (viewHeight / 2) + (contentHeight / 2)];
94
97
  return [0, .3];
95
98
  } else return [0, ...isNumber(middleHeight) ? [middleHeight] : [], modalHeight];
96
99
  }, [viewHeight, contentHeight, centered, middleHeight, modalHeight]);
@@ -173,12 +176,13 @@ export const SnapSheetModalBase = forwardRef(function SnapSheetModalBase({
173
176
 
174
177
  const centeredStyle = useMemo(() => centered ? ({
175
178
  position: 'absolute',
176
- width: viewWidth || 0,
179
+ width: viewWidth,
177
180
  left: 0,
178
- top: 0,
179
- marginTop: -(contentHeight / 2) || 0
181
+ top: 0
180
182
  }) : undefined, [centered, contentHeight, viewWidth]);
181
183
 
184
+ const inputIdIterator = useRef(0);
185
+
182
186
  const renderChild = () =>
183
187
  <View
184
188
  style={styling.absoluteFill}
@@ -197,39 +201,52 @@ export const SnapSheetModalBase = forwardRef(function SnapSheetModalBase({
197
201
  }} />
198
202
  )}
199
203
  <ReactHijacker
200
- doHijack={(node, path) => {
201
- if (isDodgeInput(node)) {
202
- const inputId = path.join('=>');
203
-
204
- return {
205
- props: {
206
- ...node.props,
207
- ref: r => {
208
- if (r) {
209
- inputRefs.current[inputId] = r;
210
- } else if (inputRefs.current[inputId]) {
211
- delete inputRefs.current[inputId];
212
- }
213
-
214
- const thatRef = node.props?.ref;
215
- if (typeof thatRef === 'function') {
216
- thatRef(r);
217
- } else if (thatRef) thatRef.current = r;
204
+ doHijack={node => {
205
+ if (!isDodgeInput(node)) return;
206
+
207
+ const renderer = () => {
208
+ const inputId = useMemo(() => `${++inputIdIterator.current}`, []);
209
+
210
+ const newProps = {
211
+ ...node.props,
212
+ ref: r => {
213
+ if (r) {
214
+ inputRefs.current[inputId] = r;
215
+ } else if (inputRefs.current[inputId]) {
216
+ delete inputRefs.current[inputId];
218
217
  }
218
+
219
+ const thatRef = node.props?.ref;
220
+ if (typeof thatRef === 'function') {
221
+ thatRef(r);
222
+ } else if (thatRef) thatRef.current = r;
219
223
  }
220
224
  };
225
+
226
+ return cloneElement(node, newProps);
221
227
  }
228
+
229
+ return createHijackedElement(
230
+ <__HijackNode>
231
+ {renderer}
232
+ </__HijackNode>
233
+ );
222
234
  }}>
223
235
  <SnapSheet
224
236
  {...restProps}
237
+ minSnapIndex={0}
225
238
  ref={sheetRef}
226
239
  snapPoints={snapPoints}
227
- {...hasClosed ? { keyboardDodgingBehaviour: 'off' } : {}}
228
240
  {...centered ? {
229
241
  style: CenteredSheetStyle,
230
- renderHandle: null
242
+ renderHandle: null,
243
+ __shaky_sheet: true,
244
+ ...isLift ? {
245
+ __checkIfElementIsFocused: r => !!r?.[CheckFocusedNode],
246
+ keyboardDodgingBehaviour: 'optimum'
247
+ } : {}
231
248
  } : {}}
232
- __shaky_sheet={centered}
249
+ {...hasClosed ? { keyboardDodgingBehaviour: 'off' } : {}}
233
250
  initialSnapIndex={Math.min(ModalState.indexOf(currentState), centered ? 1 : 2)}
234
251
  disabled={centered || disabled || disablePanGesture}
235
252
  onSnapFinish={i => {
@@ -240,14 +257,34 @@ export const SnapSheetModalBase = forwardRef(function SnapSheetModalBase({
240
257
  }}>
241
258
  {(hasClosed && (!releaseUnmount && unMountChildrenWhenClosed))
242
259
  ? null :
243
- <View style={centered ? centeredStyle : styling.flexer}>
244
- <View
245
- style={centered ? restProps.style : styling.flexer}
246
- onLayout={e => {
247
- if (centered) setContentHeight(e.nativeEvent.layout.height);
248
- }}>
260
+ <View
261
+ style={centered ? centeredStyle : styling.flexer}
262
+ onLayout={e => {
263
+ if (centered) {
264
+ const h = e.nativeEvent.layout.height;
265
+
266
+ if (contentHeight === undefined)
267
+ return setContentHeight(PixelRatio.roundToNearestPixel(h));
268
+
269
+ const pixel = PixelRatio.roundToNearestPixel(h);
270
+
271
+ if (Math.abs(contentHeight - pixel) >= 1)
272
+ setContentHeight(pixel);
273
+ }
274
+ }}>
275
+ <View style={centered ? restProps.style : styling.flexer}>
249
276
  {children}
250
277
  </View>
278
+ {isLift ?
279
+ <View
280
+ ref={r => {
281
+ if (r) {
282
+ r[CheckFocusedNode] = true;
283
+ }
284
+ }}
285
+ dodge_keyboard_input
286
+ style={styling.fakePlaceholder}
287
+ /> : null}
251
288
  </View>}
252
289
  </SnapSheet>
253
290
  </ReactHijacker>
package/src/utils.js CHANGED
@@ -1,4 +1,6 @@
1
1
 
2
2
  export const doRendable = (e, d) => e === undefined ? d : typeof e === 'function' ? e() : e;
3
3
 
4
- export const isNumber = t => typeof t === 'number' && !isNaN(t) && Number.isFinite(t);
4
+ export const isNumber = t => typeof t === 'number' && !isNaN(t) && Number.isFinite(t);
5
+
6
+ export const isPositiveNumber = t => isNumber(t) && t >= 0;