@tamagui/sheet 1.112.1 → 1.112.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.
Files changed (40) hide show
  1. package/dist/cjs/SheetImplementationCustom.js +247 -249
  2. package/dist/cjs/SheetImplementationCustom.js.map +2 -2
  3. package/dist/cjs/SheetImplementationCustom.native.js +3 -3
  4. package/dist/cjs/SheetImplementationCustom.native.js.map +2 -2
  5. package/dist/cjs/constants.js +1 -4
  6. package/dist/cjs/constants.js.map +1 -1
  7. package/dist/cjs/constants.native.js +1 -4
  8. package/dist/cjs/constants.native.js.map +2 -2
  9. package/dist/esm/SheetImplementationCustom.js +253 -252
  10. package/dist/esm/SheetImplementationCustom.js.map +1 -1
  11. package/dist/esm/SheetImplementationCustom.mjs +8 -8
  12. package/dist/esm/SheetImplementationCustom.mjs.map +1 -1
  13. package/dist/esm/SheetImplementationCustom.native.js +7 -7
  14. package/dist/esm/SheetImplementationCustom.native.js.map +1 -1
  15. package/dist/esm/constants.js +1 -4
  16. package/dist/esm/constants.js.map +1 -1
  17. package/dist/esm/constants.mjs +2 -5
  18. package/dist/esm/constants.mjs.map +1 -1
  19. package/dist/esm/constants.native.js +2 -5
  20. package/dist/esm/constants.native.js.map +1 -1
  21. package/dist/jsx/SheetImplementationCustom.js +253 -252
  22. package/dist/jsx/SheetImplementationCustom.js.map +1 -1
  23. package/dist/jsx/SheetImplementationCustom.mjs +8 -8
  24. package/dist/jsx/SheetImplementationCustom.mjs.map +1 -1
  25. package/dist/jsx/SheetImplementationCustom.native.js +6 -7
  26. package/dist/jsx/SheetImplementationCustom.native.js.map +2 -2
  27. package/dist/jsx/constants.js +1 -4
  28. package/dist/jsx/constants.js.map +1 -1
  29. package/dist/jsx/constants.mjs +2 -5
  30. package/dist/jsx/constants.mjs.map +1 -1
  31. package/dist/jsx/constants.native.js +1 -4
  32. package/dist/jsx/constants.native.js.map +2 -2
  33. package/package.json +16 -16
  34. package/src/SheetImplementationCustom.tsx +442 -433
  35. package/src/constants.tsx +0 -9
  36. package/src/createSheet.tsx +1 -1
  37. package/types/SheetImplementationCustom.d.ts +2 -2
  38. package/types/SheetImplementationCustom.d.ts.map +1 -1
  39. package/types/constants.d.ts +0 -1
  40. package/types/constants.d.ts.map +1 -1
@@ -1,19 +1,23 @@
1
- import React from 'react'
2
1
  import { AdaptParentContext } from '@tamagui/adapt'
3
2
  import { AnimatePresence } from '@tamagui/animate-presence'
4
3
  import { useComposedRefs } from '@tamagui/compose-refs'
5
- import { currentPlatform, isWeb, useIsomorphicLayoutEffect } from '@tamagui/constants'
4
+ import {
5
+ currentPlatform,
6
+ isClient,
7
+ isWeb,
8
+ useIsomorphicLayoutEffect,
9
+ } from '@tamagui/constants'
6
10
  import {
7
11
  getConfig,
8
12
  Stack,
9
13
  Theme,
10
- themeable,
11
14
  useConfiguration,
12
15
  useEvent,
13
16
  useThemeName,
14
17
  } from '@tamagui/core'
15
18
  import { Portal } from '@tamagui/portal'
16
19
  import { useKeyboardVisible } from '@tamagui/use-keyboard-visible'
20
+ import React from 'react'
17
21
  import type {
18
22
  Animated,
19
23
  GestureResponderEvent,
@@ -21,8 +25,6 @@ import type {
21
25
  PanResponderGestureState,
22
26
  } from 'react-native'
23
27
  import { Dimensions, Keyboard, PanResponder, View } from 'react-native'
24
-
25
- import { SHEET_HIDDEN_STYLESHEET } from './constants'
26
28
  import { ParentSheetContext, SheetInsideSheetContext } from './contexts'
27
29
  import { resisted } from './helpers'
28
30
  import { SheetProvider } from './SheetContext'
@@ -32,488 +34,495 @@ import { useSheetProviderProps } from './useSheetProviderProps'
32
34
 
33
35
  let hiddenSize = 10_000.1
34
36
 
35
- export const SheetImplementationCustom = themeable(
36
- React.forwardRef<View, SheetProps>(
37
- function SheetImplementationCustom(props, forwardedRef) {
38
- const parentSheet = React.useContext(ParentSheetContext)
39
-
40
- const {
41
- animation,
42
- animationConfig: animationConfigProp,
43
- modal = false,
44
- zIndex = parentSheet.zIndex + 1,
45
- moveOnKeyboardChange = false,
46
- unmountChildrenWhenHidden = false,
47
- portalProps,
48
- containerComponent: ContainerComponent = React.Fragment,
49
- } = props
50
-
51
- const keyboardIsVisible = useKeyboardVisible()
52
- const state = useSheetOpenState(props)
53
- const [overlayComponent, setOverlayComponent] = React.useState<any>(null)
54
-
55
- const providerProps = useSheetProviderProps(props, state, {
56
- onOverlayComponent: setOverlayComponent,
57
- })
58
- const {
59
- frameSize,
60
- setFrameSize,
61
- snapPoints,
62
- snapPointsMode,
63
- hasFit,
64
- position,
65
- setPosition,
66
- scrollBridge,
67
- screenSize,
68
- setMaxContentSize,
69
- maxSnapPoint,
70
- } = providerProps
71
- const { open, controller, isHidden } = state
72
-
73
- const sheetRef = React.useRef<View>(null)
74
- const ref = useComposedRefs(forwardedRef, sheetRef)
75
-
76
- // TODO this can be extracted into a helper getAnimationConfig(animationProp as array | string)
77
- const animationConfig = (() => {
78
- const [animationProp, animationPropConfig] = !animation
79
- ? []
80
- : Array.isArray(animation)
81
- ? animation
82
- : ([animation] as const)
83
- return (
84
- animationConfigProp ??
85
- (animationProp
86
- ? {
87
- ...(getConfig().animations.animations[animationProp as string] as Object),
88
- ...animationPropConfig,
89
- }
90
- : null)
91
- )
92
- })()
93
-
94
- /**
95
- * This is a hacky workaround for native:
96
- */
97
- const [isShowingInnerSheet, setIsShowingInnerSheet] = React.useState(false)
98
- const shouldHideParentSheet = !isWeb && modal && isShowingInnerSheet
99
- const parentSheetContext = React.useContext(SheetInsideSheetContext)
100
- const onInnerSheet = React.useCallback((hasChild: boolean) => {
101
- setIsShowingInnerSheet(hasChild)
102
- }, [])
103
-
104
- const positions = React.useMemo(
105
- () =>
106
- snapPoints.map((point) =>
107
- getYPositions(snapPointsMode, point, screenSize, frameSize)
108
- ),
109
- [screenSize, frameSize, snapPoints, snapPointsMode]
110
- )
111
-
112
- const { animationDriver } = useConfiguration()
113
- const { useAnimatedNumber, useAnimatedNumberStyle, useAnimatedNumberReaction } =
114
- animationDriver
115
- const AnimatedView = (animationDriver.View ?? Stack) as typeof Animated.View
116
-
117
- useIsomorphicLayoutEffect(() => {
118
- if (!(parentSheetContext && open)) return
119
- parentSheetContext(true)
120
- return () => {
121
- parentSheetContext(false)
122
- }
123
- }, [parentSheetContext, open])
124
-
125
- const nextParentContext = React.useMemo(
126
- () => ({
127
- zIndex,
128
- }),
129
- [zIndex]
37
+ let sheetHiddenStyleSheet: HTMLStyleElement | null = null
38
+
39
+ export const SheetImplementationCustom = React.forwardRef<View, SheetProps>(
40
+ function SheetImplementationCustom(props, forwardedRef) {
41
+ const parentSheet = React.useContext(ParentSheetContext)
42
+
43
+ const {
44
+ animation,
45
+ animationConfig: animationConfigProp,
46
+ modal = false,
47
+ zIndex = parentSheet.zIndex + 1,
48
+ moveOnKeyboardChange = false,
49
+ unmountChildrenWhenHidden = false,
50
+ portalProps,
51
+ containerComponent: ContainerComponent = React.Fragment,
52
+ } = props
53
+
54
+ const keyboardIsVisible = useKeyboardVisible()
55
+ const state = useSheetOpenState(props)
56
+ const [overlayComponent, setOverlayComponent] = React.useState<any>(null)
57
+
58
+ const providerProps = useSheetProviderProps(props, state, {
59
+ onOverlayComponent: setOverlayComponent,
60
+ })
61
+ const {
62
+ frameSize,
63
+ setFrameSize,
64
+ snapPoints,
65
+ snapPointsMode,
66
+ hasFit,
67
+ position,
68
+ setPosition,
69
+ scrollBridge,
70
+ screenSize,
71
+ setMaxContentSize,
72
+ maxSnapPoint,
73
+ } = providerProps
74
+ const { open, controller, isHidden } = state
75
+
76
+ const sheetRef = React.useRef<View>(null)
77
+ const ref = useComposedRefs(forwardedRef, sheetRef)
78
+
79
+ // TODO this can be extracted into a helper getAnimationConfig(animationProp as array | string)
80
+ const animationConfig = (() => {
81
+ const [animationProp, animationPropConfig] = !animation
82
+ ? []
83
+ : Array.isArray(animation)
84
+ ? animation
85
+ : ([animation] as const)
86
+ return (
87
+ animationConfigProp ??
88
+ (animationProp
89
+ ? {
90
+ ...(getConfig().animations.animations[animationProp as string] as Object),
91
+ ...animationPropConfig,
92
+ }
93
+ : null)
130
94
  )
131
-
132
- const animatedNumber = useAnimatedNumber(hiddenSize)
133
- const at = React.useRef(hiddenSize)
134
-
135
- useAnimatedNumberReaction(
136
- {
137
- value: animatedNumber,
138
- hostRef: sheetRef,
95
+ })()
96
+
97
+ /**
98
+ * This is a hacky workaround for native:
99
+ */
100
+ const [isShowingInnerSheet, setIsShowingInnerSheet] = React.useState(false)
101
+ const shouldHideParentSheet = !isWeb && modal && isShowingInnerSheet
102
+ const parentSheetContext = React.useContext(SheetInsideSheetContext)
103
+ const onInnerSheet = React.useCallback((hasChild: boolean) => {
104
+ setIsShowingInnerSheet(hasChild)
105
+ }, [])
106
+
107
+ const positions = React.useMemo(
108
+ () =>
109
+ snapPoints.map((point) =>
110
+ getYPositions(snapPointsMode, point, screenSize, frameSize)
111
+ ),
112
+ [screenSize, frameSize, snapPoints, snapPointsMode]
113
+ )
114
+
115
+ const { animationDriver } = useConfiguration()
116
+ const { useAnimatedNumber, useAnimatedNumberStyle, useAnimatedNumberReaction } =
117
+ animationDriver
118
+ const AnimatedView = (animationDriver.View ?? Stack) as typeof Animated.View
119
+
120
+ useIsomorphicLayoutEffect(() => {
121
+ if (!(parentSheetContext && open)) return
122
+ parentSheetContext(true)
123
+ return () => {
124
+ parentSheetContext(false)
125
+ }
126
+ }, [parentSheetContext, open])
127
+
128
+ const nextParentContext = React.useMemo(
129
+ () => ({
130
+ zIndex,
131
+ }),
132
+ [zIndex]
133
+ )
134
+
135
+ const animatedNumber = useAnimatedNumber(hiddenSize)
136
+ const at = React.useRef(hiddenSize)
137
+
138
+ useAnimatedNumberReaction(
139
+ {
140
+ value: animatedNumber,
141
+ hostRef: sheetRef,
142
+ },
143
+ React.useCallback(
144
+ (value) => {
145
+ at.current = value
146
+ scrollBridge.paneY = value
139
147
  },
140
- React.useCallback(
141
- (value) => {
142
- at.current = value
143
- scrollBridge.paneY = value
144
- },
145
- [animationDriver]
146
- )
148
+ [animationDriver]
147
149
  )
150
+ )
148
151
 
149
- function stopSpring() {
150
- animatedNumber.stop()
151
- if (scrollBridge.onFinishAnimate) {
152
- scrollBridge.onFinishAnimate()
153
- scrollBridge.onFinishAnimate = undefined
154
- }
152
+ function stopSpring() {
153
+ animatedNumber.stop()
154
+ if (scrollBridge.onFinishAnimate) {
155
+ scrollBridge.onFinishAnimate()
156
+ scrollBridge.onFinishAnimate = undefined
155
157
  }
158
+ }
156
159
 
157
- const hasntMeasured = at.current === hiddenSize
158
-
159
- const animateTo = useEvent((position: number) => {
160
- if (frameSize === 0) return
161
-
162
- let toValue = isHidden || position === -1 ? screenSize : positions[position]
160
+ const hasntMeasured = at.current === hiddenSize
163
161
 
164
- if (at.current === toValue) return
165
- at.current = toValue
162
+ const animateTo = useEvent((position: number) => {
163
+ if (frameSize === 0) return
166
164
 
167
- stopSpring()
165
+ let toValue = isHidden || position === -1 ? screenSize : positions[position]
168
166
 
169
- if (hasntMeasured || isHidden) {
170
- // first run, we need to set to screen size before running
171
- animatedNumber.setValue(
172
- screenSize,
173
- {
174
- type: 'timing',
175
- duration: 0,
176
- },
177
- () => {
178
- if (isHidden) {
179
- return
180
- }
181
-
182
- toValue = positions[position]
183
- at.current = toValue
184
-
185
- animatedNumber.setValue(toValue, {
186
- type: 'spring',
187
- ...animationConfig,
188
- })
189
- }
190
- )
191
- return
192
- }
167
+ if (at.current === toValue) return
168
+ at.current = toValue
193
169
 
194
- animatedNumber.setValue(toValue, {
195
- type: 'spring',
196
- ...animationConfig,
197
- })
198
- })
170
+ stopSpring()
199
171
 
200
- useIsomorphicLayoutEffect(() => {
201
- if (screenSize && hasntMeasured) {
202
- animatedNumber.setValue(screenSize, {
172
+ if (hasntMeasured || isHidden) {
173
+ // first run, we need to set to screen size before running
174
+ animatedNumber.setValue(
175
+ screenSize,
176
+ {
203
177
  type: 'timing',
204
178
  duration: 0,
205
- })
206
- }
207
- }, [hasntMeasured, screenSize])
208
-
209
- useIsomorphicLayoutEffect(() => {
210
- if (!frameSize || !screenSize || isHidden || (hasntMeasured && !open)) {
211
- return
212
- }
213
- animateTo(position)
214
- }, [isHidden, frameSize, screenSize, open, position])
179
+ },
180
+ () => {
181
+ if (isHidden) {
182
+ return
183
+ }
215
184
 
216
- const disableDrag = props.disableDrag ?? controller?.disableDrag
217
- const themeName = useThemeName()
218
- const [isDragging, setIsDragging] = React.useState(false)
185
+ toValue = positions[position]
186
+ at.current = toValue
219
187
 
220
- const panResponder = React.useMemo(() => {
221
- if (disableDrag) return
222
- if (!frameSize) return
223
- if (isShowingInnerSheet) return
188
+ animatedNumber.setValue(toValue, {
189
+ type: 'spring',
190
+ ...animationConfig,
191
+ })
192
+ }
193
+ )
194
+ return
195
+ }
224
196
 
225
- const minY = positions[0]
226
- scrollBridge.paneMinY = minY
227
- let startY = at.current
197
+ animatedNumber.setValue(toValue, {
198
+ type: 'spring',
199
+ ...animationConfig,
200
+ })
201
+ })
228
202
 
229
- function setPanning(val: boolean) {
230
- setIsDragging(val)
203
+ useIsomorphicLayoutEffect(() => {
204
+ if (screenSize && hasntMeasured) {
205
+ animatedNumber.setValue(screenSize, {
206
+ type: 'timing',
207
+ duration: 0,
208
+ })
209
+ }
210
+ }, [hasntMeasured, screenSize])
231
211
 
232
- // make unselectable:
233
- if (!SHEET_HIDDEN_STYLESHEET) return
212
+ useIsomorphicLayoutEffect(() => {
213
+ if (!frameSize || !screenSize || isHidden || (hasntMeasured && !open)) {
214
+ return
215
+ }
216
+ animateTo(position)
217
+ }, [isHidden, frameSize, screenSize, open, position])
218
+
219
+ const disableDrag = props.disableDrag ?? controller?.disableDrag
220
+ const themeName = useThemeName()
221
+ const [isDragging, setIsDragging] = React.useState(false)
222
+
223
+ const panResponder = React.useMemo(() => {
224
+ if (disableDrag) return
225
+ if (!frameSize) return
226
+ if (isShowingInnerSheet) return
227
+
228
+ const minY = positions[0]
229
+ scrollBridge.paneMinY = minY
230
+ let startY = at.current
231
+
232
+ function setPanning(val: boolean) {
233
+ setIsDragging(val)
234
+
235
+ // make unselectable:
236
+ if (isClient) {
237
+ if (!sheetHiddenStyleSheet) {
238
+ sheetHiddenStyleSheet = document.createElement('style')
239
+ if (typeof document.head !== 'undefined') {
240
+ document.head.appendChild(sheetHiddenStyleSheet)
241
+ }
242
+ }
234
243
  if (!val) {
235
- SHEET_HIDDEN_STYLESHEET.innerText = ''
244
+ sheetHiddenStyleSheet.innerText = ''
236
245
  } else {
237
- SHEET_HIDDEN_STYLESHEET.innerText =
246
+ sheetHiddenStyleSheet.innerText =
238
247
  ':root * { user-select: none !important; -webkit-user-select: none !important; }'
239
248
  }
240
249
  }
250
+ }
241
251
 
242
- const release = ({ vy, dragAt }: { dragAt: number; vy: number }) => {
243
- isExternalDrag = false
244
- previouslyScrolling = false
245
- setPanning(false)
246
- const at = dragAt + startY
247
- // seems liky vy goes up to about 4 at the very most (+ is down, - is up)
248
- // lets base our multiplier on the total layout height
249
- const end = at + frameSize * vy * 0.2
250
- let closestPoint = 0
251
- let dist = Number.POSITIVE_INFINITY
252
- for (let i = 0; i < positions.length; i++) {
253
- const position = positions[i]
254
- const curDist = end > position ? end - position : position - end
255
- if (curDist < dist) {
256
- dist = curDist
257
- closestPoint = i
258
- }
252
+ const release = ({ vy, dragAt }: { dragAt: number; vy: number }) => {
253
+ isExternalDrag = false
254
+ previouslyScrolling = false
255
+ setPanning(false)
256
+ const at = dragAt + startY
257
+ // seems liky vy goes up to about 4 at the very most (+ is down, - is up)
258
+ // lets base our multiplier on the total layout height
259
+ const end = at + frameSize * vy * 0.2
260
+ let closestPoint = 0
261
+ let dist = Number.POSITIVE_INFINITY
262
+ for (let i = 0; i < positions.length; i++) {
263
+ const position = positions[i]
264
+ const curDist = end > position ? end - position : position - end
265
+ if (curDist < dist) {
266
+ dist = curDist
267
+ closestPoint = i
259
268
  }
260
- // have to call both because state may not change but need to snap back
261
- setPosition(closestPoint)
262
- animateTo(closestPoint)
263
269
  }
270
+ // have to call both because state may not change but need to snap back
271
+ setPosition(closestPoint)
272
+ animateTo(closestPoint)
273
+ }
264
274
 
265
- const finish = (_e: GestureResponderEvent, state: PanResponderGestureState) => {
266
- release({
267
- vy: state.vy,
268
- dragAt: state.dy,
269
- })
270
- }
275
+ const finish = (_e: GestureResponderEvent, state: PanResponderGestureState) => {
276
+ release({
277
+ vy: state.vy,
278
+ dragAt: state.dy,
279
+ })
280
+ }
271
281
 
272
- let previouslyScrolling = false
282
+ let previouslyScrolling = false
273
283
 
274
- const onMoveShouldSet = (
275
- e: GestureResponderEvent,
276
- { dy }: PanResponderGestureState
277
- ) => {
278
- // if dragging handle always allow:
279
- if (e.target === providerProps.handleRef.current) {
280
- return true
281
- }
284
+ const onMoveShouldSet = (
285
+ e: GestureResponderEvent,
286
+ { dy }: PanResponderGestureState
287
+ ) => {
288
+ // if dragging handle always allow:
289
+ if (e.target === providerProps.handleRef.current) {
290
+ return true
291
+ }
282
292
 
283
- const isScrolled = scrollBridge.y !== 0
284
- const isDraggingUp = dy < 0
285
- // we can treat near top instead of exactly to avoid trouble with springs
286
- const isNearTop = scrollBridge.paneY - 5 <= scrollBridge.paneMinY
287
- if (isScrolled) {
288
- previouslyScrolling = true
289
- return false
290
- }
291
- // prevent drag once at top and pulling up
292
- if (isNearTop) {
293
- if (!isScrolled && isDraggingUp) {
294
- // TODO: pulling past the limit breaks scroll on native, need to better make ScrollView
295
- if (!isWeb) {
296
- return false
297
- }
293
+ const isScrolled = scrollBridge.y !== 0
294
+ const isDraggingUp = dy < 0
295
+ // we can treat near top instead of exactly to avoid trouble with springs
296
+ const isNearTop = scrollBridge.paneY - 5 <= scrollBridge.paneMinY
297
+ if (isScrolled) {
298
+ previouslyScrolling = true
299
+ return false
300
+ }
301
+ // prevent drag once at top and pulling up
302
+ if (isNearTop) {
303
+ if (!isScrolled && isDraggingUp) {
304
+ // TODO: pulling past the limit breaks scroll on native, need to better make ScrollView
305
+ if (!isWeb) {
306
+ return false
298
307
  }
299
308
  }
300
- // we could do some detection of other touchables and cancel here..
301
- return Math.abs(dy) > 5
302
309
  }
310
+ // we could do some detection of other touchables and cancel here..
311
+ return Math.abs(dy) > 5
312
+ }
303
313
 
304
- const grant = () => {
305
- setPanning(true)
306
- stopSpring()
307
- startY = at.current
308
- }
314
+ const grant = () => {
315
+ setPanning(true)
316
+ stopSpring()
317
+ startY = at.current
318
+ }
309
319
 
310
- let isExternalDrag = false
320
+ let isExternalDrag = false
311
321
 
312
- scrollBridge.drag = (dy) => {
313
- if (!isExternalDrag) {
314
- isExternalDrag = true
315
- grant()
316
- }
317
- const to = dy + startY
318
- animatedNumber.setValue(resisted(to, minY), { type: 'direct' })
322
+ scrollBridge.drag = (dy) => {
323
+ if (!isExternalDrag) {
324
+ isExternalDrag = true
325
+ grant()
319
326
  }
327
+ const to = dy + startY
328
+ animatedNumber.setValue(resisted(to, minY), { type: 'direct' })
329
+ }
320
330
 
321
- scrollBridge.release = release
331
+ scrollBridge.release = release
322
332
 
323
- return PanResponder.create({
324
- onMoveShouldSetPanResponder: onMoveShouldSet,
325
- onPanResponderGrant: grant,
326
- onPanResponderMove: (_e, { dy }) => {
327
- const toFull = dy + startY
328
- const to = resisted(toFull, minY)
329
- animatedNumber.setValue(to, { type: 'direct' })
330
- },
331
- onPanResponderEnd: finish,
332
- onPanResponderTerminate: finish,
333
- onPanResponderRelease: finish,
334
- })
335
- }, [disableDrag, isShowingInnerSheet, animateTo, frameSize, positions, setPosition])
336
-
337
- const handleAnimationViewLayout = React.useCallback(
338
- (e: LayoutChangeEvent) => {
339
- // avoid bugs where it grows forever for whatever reason
340
- const next = Math.min(
341
- e.nativeEvent?.layout.height,
342
- Dimensions.get('screen').height
343
- )
344
- if (!next) return
345
- setFrameSize(next)
333
+ return PanResponder.create({
334
+ onMoveShouldSetPanResponder: onMoveShouldSet,
335
+ onPanResponderGrant: grant,
336
+ onPanResponderMove: (_e, { dy }) => {
337
+ const toFull = dy + startY
338
+ const to = resisted(toFull, minY)
339
+ animatedNumber.setValue(to, { type: 'direct' })
346
340
  },
347
- [keyboardIsVisible]
348
- )
349
-
350
- const handleMaxContentViewLayout = React.useCallback(
351
- (e: LayoutChangeEvent) => {
352
- // avoid bugs where it grows forever for whatever reason
353
- const next = Math.min(
354
- e.nativeEvent?.layout.height,
355
- Dimensions.get('screen').height
341
+ onPanResponderEnd: finish,
342
+ onPanResponderTerminate: finish,
343
+ onPanResponderRelease: finish,
344
+ })
345
+ }, [disableDrag, isShowingInnerSheet, animateTo, frameSize, positions, setPosition])
346
+
347
+ const handleAnimationViewLayout = React.useCallback(
348
+ (e: LayoutChangeEvent) => {
349
+ // avoid bugs where it grows forever for whatever reason
350
+ const next = Math.min(
351
+ e.nativeEvent?.layout.height,
352
+ Dimensions.get('screen').height
353
+ )
354
+ if (!next) return
355
+ setFrameSize(next)
356
+ },
357
+ [keyboardIsVisible]
358
+ )
359
+
360
+ const handleMaxContentViewLayout = React.useCallback(
361
+ (e: LayoutChangeEvent) => {
362
+ // avoid bugs where it grows forever for whatever reason
363
+ const next = Math.min(
364
+ e.nativeEvent?.layout.height,
365
+ Dimensions.get('screen').height
366
+ )
367
+ if (!next) return
368
+ setMaxContentSize(next)
369
+ },
370
+ [keyboardIsVisible]
371
+ )
372
+
373
+ const animatedStyle = useAnimatedNumberStyle(animatedNumber, (val) => {
374
+ 'worklet'
375
+ const translateY = frameSize === 0 ? hiddenSize : val
376
+
377
+ return {
378
+ transform: [{ translateY }],
379
+ }
380
+ })
381
+
382
+ const sizeBeforeKeyboard = React.useRef<number | null>(null)
383
+ React.useEffect(() => {
384
+ if (isWeb || !moveOnKeyboardChange) return
385
+ const keyboardShowListener = Keyboard.addListener(
386
+ currentPlatform === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow',
387
+ (e) => {
388
+ if (sizeBeforeKeyboard.current !== null) return
389
+ sizeBeforeKeyboard.current =
390
+ isHidden || position === -1 ? screenSize : positions[position]
391
+ animatedNumber.setValue(
392
+ Math.max(sizeBeforeKeyboard.current - e.endCoordinates.height, 0),
393
+ {
394
+ type: 'timing',
395
+ duration: 250,
396
+ }
356
397
  )
357
- if (!next) return
358
- setMaxContentSize(next)
359
- },
360
- [keyboardIsVisible]
361
- )
362
-
363
- const animatedStyle = useAnimatedNumberStyle(animatedNumber, (val) => {
364
- 'worklet'
365
- const translateY = frameSize === 0 ? hiddenSize : val
366
-
367
- return {
368
- transform: [{ translateY }],
369
398
  }
399
+ )
400
+ const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => {
401
+ if (sizeBeforeKeyboard.current === null) return
402
+ animatedNumber.setValue(sizeBeforeKeyboard.current, {
403
+ type: 'timing',
404
+ duration: 250,
405
+ })
406
+ sizeBeforeKeyboard.current = null
370
407
  })
371
408
 
372
- const sizeBeforeKeyboard = React.useRef<number | null>(null)
373
- React.useEffect(() => {
374
- if (isWeb || !moveOnKeyboardChange) return
375
- const keyboardShowListener = Keyboard.addListener(
376
- currentPlatform === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow',
377
- (e) => {
378
- if (sizeBeforeKeyboard.current !== null) return
379
- sizeBeforeKeyboard.current =
380
- isHidden || position === -1 ? screenSize : positions[position]
381
- animatedNumber.setValue(
382
- Math.max(sizeBeforeKeyboard.current - e.endCoordinates.height, 0),
383
- {
384
- type: 'timing',
385
- duration: 250,
386
- }
387
- )
388
- }
389
- )
390
- const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => {
391
- if (sizeBeforeKeyboard.current === null) return
392
- animatedNumber.setValue(sizeBeforeKeyboard.current, {
393
- type: 'timing',
394
- duration: 250,
395
- })
396
- sizeBeforeKeyboard.current = null
397
- })
409
+ return () => {
410
+ keyboardDidHideListener.remove()
411
+ keyboardShowListener.remove()
412
+ }
413
+ }, [moveOnKeyboardChange, positions, position, isHidden])
398
414
 
415
+ // we need to set this *after* fully closed to 0, to avoid it overlapping
416
+ // the page when resizing quickly on web for example
417
+ const [opacity, setOpacity] = React.useState(open ? 1 : 0)
418
+ if (open && opacity === 0) {
419
+ setOpacity(1)
420
+ }
421
+ React.useEffect(() => {
422
+ if (!open) {
423
+ // need to wait for animation complete, for now lets just do it naively
424
+ const tm = setTimeout(() => {
425
+ setOpacity(0)
426
+ }, 400)
399
427
  return () => {
400
- keyboardDidHideListener.remove()
401
- keyboardShowListener.remove()
428
+ clearTimeout(tm)
402
429
  }
403
- }, [moveOnKeyboardChange, positions, position, isHidden])
404
-
405
- // we need to set this *after* fully closed to 0, to avoid it overlapping
406
- // the page when resizing quickly on web for example
407
- const [opacity, setOpacity] = React.useState(open ? 1 : 0)
408
- if (open && opacity === 0) {
409
- setOpacity(1)
410
430
  }
411
- React.useEffect(() => {
412
- if (!open) {
413
- // need to wait for animation complete, for now lets just do it naively
414
- const tm = setTimeout(() => {
415
- setOpacity(0)
416
- }, 400)
417
- return () => {
418
- clearTimeout(tm)
419
- }
420
- }
421
- }, [open])
422
-
423
- const forcedContentHeight = hasFit
424
- ? undefined
425
- : snapPointsMode === 'percent'
426
- ? `${maxSnapPoint}${isWeb ? 'dvh' : '%'}`
427
- : maxSnapPoint
428
-
429
- const contents = (
430
- <ParentSheetContext.Provider value={nextParentContext}>
431
- <SheetProvider {...providerProps}>
432
- <AnimatePresence custom={{ open }}>
433
- {shouldHideParentSheet || !open ? null : overlayComponent}
434
- </AnimatePresence>
435
-
436
- {snapPointsMode !== 'percent' && (
437
- <View
438
- style={{
439
- opacity: 0,
440
- position: 'absolute',
441
- top: 0,
442
- left: 0,
443
- right: 0,
444
- bottom: 0,
431
+ }, [open])
432
+
433
+ const forcedContentHeight = hasFit
434
+ ? undefined
435
+ : snapPointsMode === 'percent'
436
+ ? `${maxSnapPoint}${isWeb ? 'dvh' : '%'}`
437
+ : maxSnapPoint
438
+
439
+ const contents = (
440
+ <ParentSheetContext.Provider value={nextParentContext}>
441
+ <SheetProvider {...providerProps}>
442
+ <AnimatePresence custom={{ open }}>
443
+ {shouldHideParentSheet || !open ? null : overlayComponent}
444
+ </AnimatePresence>
445
+
446
+ {snapPointsMode !== 'percent' && (
447
+ <View
448
+ style={{
449
+ opacity: 0,
450
+ position: 'absolute',
451
+ top: 0,
452
+ left: 0,
453
+ right: 0,
454
+ bottom: 0,
455
+ pointerEvents: 'none',
456
+ }}
457
+ onLayout={handleMaxContentViewLayout}
458
+ />
459
+ )}
460
+
461
+ <AnimatedView
462
+ ref={ref}
463
+ {...panResponder?.panHandlers}
464
+ onLayout={handleAnimationViewLayout}
465
+ {...(!isDragging && {
466
+ // @ts-ignore for CSS driver this is necessary to attach the transition
467
+ animation,
468
+ })}
469
+ // @ts-ignore
470
+ disableClassName
471
+ style={[
472
+ {
473
+ position: 'absolute',
474
+ zIndex,
475
+ width: '100%',
476
+ height: forcedContentHeight,
477
+ minHeight: forcedContentHeight,
478
+ opacity,
479
+ ...((shouldHideParentSheet || !open) && {
445
480
  pointerEvents: 'none',
446
- }}
447
- onLayout={handleMaxContentViewLayout}
448
- />
449
- )}
450
-
451
- <AnimatedView
452
- ref={ref}
453
- {...panResponder?.panHandlers}
454
- onLayout={handleAnimationViewLayout}
455
- {...(!isDragging && {
456
- // @ts-ignore for CSS driver this is necessary to attach the transition
457
- animation,
458
- })}
459
- // @ts-ignore
460
- disableClassName
461
- style={[
462
- {
463
- position: 'absolute',
464
- zIndex,
465
- width: '100%',
466
- height: forcedContentHeight,
467
- minHeight: forcedContentHeight,
468
- opacity,
469
- ...((shouldHideParentSheet || !open) && {
470
- pointerEvents: 'none',
471
- }),
472
- },
473
- animatedStyle,
474
- ]}
475
- >
476
- {props.children}
477
- </AnimatedView>
478
- </SheetProvider>
479
- </ParentSheetContext.Provider>
481
+ }),
482
+ },
483
+ animatedStyle,
484
+ ]}
485
+ >
486
+ {props.children}
487
+ </AnimatedView>
488
+ </SheetProvider>
489
+ </ParentSheetContext.Provider>
490
+ )
491
+
492
+ const adaptContext = React.useContext(AdaptParentContext)
493
+
494
+ // start mounted so we get an accurate measurement the first time
495
+ const shouldMountChildren = Boolean(opacity || !unmountChildrenWhenHidden)
496
+
497
+ if (modal) {
498
+ const modalContents = (
499
+ <Portal zIndex={zIndex} {...portalProps}>
500
+ {shouldMountChildren && (
501
+ <ContainerComponent>
502
+ <Theme forceClassName name={themeName}>
503
+ <AdaptParentContext.Provider value={adaptContext}>
504
+ {contents}
505
+ </AdaptParentContext.Provider>
506
+ </Theme>
507
+ </ContainerComponent>
508
+ )}
509
+ </Portal>
480
510
  )
481
511
 
482
- const adaptContext = React.useContext(AdaptParentContext)
483
-
484
- // start mounted so we get an accurate measurement the first time
485
- const shouldMountChildren = Boolean(opacity || !unmountChildrenWhenHidden)
486
-
487
- if (modal) {
488
- const modalContents = (
489
- <Portal zIndex={zIndex} {...portalProps}>
490
- {shouldMountChildren && (
491
- <ContainerComponent>
492
- <Theme forceClassName name={themeName}>
493
- <AdaptParentContext.Provider value={adaptContext}>
494
- {contents}
495
- </AdaptParentContext.Provider>
496
- </Theme>
497
- </ContainerComponent>
498
- )}
499
- </Portal>
500
- )
501
-
502
- if (isWeb) {
503
- return modalContents
504
- }
505
-
506
- // on native we don't support multiple modals yet... fix for now is to hide outer one
507
- return (
508
- <SheetInsideSheetContext.Provider value={onInnerSheet}>
509
- {modalContents}
510
- </SheetInsideSheetContext.Provider>
511
- )
512
+ if (isWeb) {
513
+ return modalContents
512
514
  }
513
515
 
514
- return contents
516
+ // on native we don't support multiple modals yet... fix for now is to hide outer one
517
+ return (
518
+ <SheetInsideSheetContext.Provider value={onInnerSheet}>
519
+ {modalContents}
520
+ </SheetInsideSheetContext.Provider>
521
+ )
515
522
  }
516
- )
523
+
524
+ return contents
525
+ }
517
526
  )
518
527
 
519
528
  function getYPositions(