@tamagui/sheet 1.0.1-beta.133 → 1.0.1-beta.136
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/cjs/RemoveScrollProps.js +17 -0
- package/dist/cjs/RemoveScrollProps.js.map +7 -0
- package/dist/cjs/SHEET_HANDLE_NAME.js +35 -0
- package/dist/cjs/SHEET_HANDLE_NAME.js.map +7 -0
- package/dist/cjs/Sheet.js +61 -118
- package/dist/cjs/Sheet.js.map +2 -2
- package/dist/cjs/SheetContext.js +38 -0
- package/dist/cjs/SheetContext.js.map +7 -0
- package/dist/cjs/SheetScrollView.js +115 -0
- package/dist/cjs/SheetScrollView.js.map +7 -0
- package/dist/cjs/types.js +17 -0
- package/dist/cjs/types.js.map +7 -0
- package/dist/esm/RemoveScrollProps.js +1 -0
- package/dist/esm/RemoveScrollProps.js.map +7 -0
- package/dist/esm/SHEET_HANDLE_NAME.js +9 -0
- package/dist/esm/SHEET_HANDLE_NAME.js.map +7 -0
- package/dist/esm/Sheet.js +54 -113
- package/dist/esm/Sheet.js.map +2 -2
- package/dist/esm/SheetContext.js +11 -0
- package/dist/esm/SheetContext.js.map +7 -0
- package/dist/esm/SheetScrollView.js +88 -0
- package/dist/esm/SheetScrollView.js.map +7 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/types.js.map +7 -0
- package/dist/jsx/RemoveScrollProps.js +1 -0
- package/dist/jsx/RemoveScrollProps.js.map +7 -0
- package/dist/jsx/SHEET_HANDLE_NAME.js +9 -0
- package/dist/jsx/SHEET_HANDLE_NAME.js.map +7 -0
- package/dist/jsx/Sheet.js +58 -101
- package/dist/jsx/Sheet.js.map +2 -2
- package/dist/jsx/SheetContext.js +11 -0
- package/dist/jsx/SheetContext.js.map +7 -0
- package/dist/jsx/SheetScrollView.js +77 -0
- package/dist/jsx/SheetScrollView.js.map +7 -0
- package/dist/jsx/types.js +1 -0
- package/dist/jsx/types.js.map +7 -0
- package/package.json +9 -8
- package/src/SHEET_HANDLE_NAME.tsx +4 -0
- package/src/Sheet.tsx +82 -203
- package/src/SheetContext.tsx +25 -0
- package/src/SheetScrollView.tsx +109 -0
- package/src/types.tsx +44 -0
- package/types/SHEET_HANDLE_NAME.d.ts +4 -0
- package/types/Sheet.d.ts +21 -56
- package/types/SheetContext.d.ts +36 -0
- package/types/SheetScrollView.d.ts +4 -0
- package/types/types.d.ts +40 -0
package/src/Sheet.tsx
CHANGED
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
Slot,
|
|
5
5
|
TamaguiElement,
|
|
6
6
|
Theme,
|
|
7
|
-
composeEventHandlers,
|
|
8
7
|
isClient,
|
|
9
8
|
isWeb,
|
|
10
9
|
mergeEvent,
|
|
@@ -16,13 +15,11 @@ import {
|
|
|
16
15
|
useThemeName,
|
|
17
16
|
withStaticProperties,
|
|
18
17
|
} from '@tamagui/core'
|
|
19
|
-
import { ScopedProps, createContextScope } from '@tamagui/create-context'
|
|
20
18
|
import { Portal } from '@tamagui/portal'
|
|
21
19
|
import { RemoveScroll } from '@tamagui/remove-scroll'
|
|
22
20
|
import { XStack, XStackProps, YStack, YStackProps } from '@tamagui/stacks'
|
|
23
21
|
import { useControllableState } from '@tamagui/use-controllable-state'
|
|
24
22
|
import React, {
|
|
25
|
-
ReactNode,
|
|
26
23
|
createContext,
|
|
27
24
|
forwardRef,
|
|
28
25
|
isValidElement,
|
|
@@ -36,74 +33,18 @@ import React, {
|
|
|
36
33
|
import {
|
|
37
34
|
Animated,
|
|
38
35
|
GestureResponderEvent,
|
|
39
|
-
NativeScrollEvent,
|
|
40
|
-
NativeSyntheticEvent,
|
|
41
36
|
PanResponder,
|
|
42
37
|
PanResponderGestureState,
|
|
43
|
-
|
|
44
|
-
ScrollViewProps,
|
|
38
|
+
StyleSheet,
|
|
45
39
|
View,
|
|
46
40
|
} from 'react-native'
|
|
47
41
|
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
import { SHEET_HANDLE_NAME, SHEET_NAME } from './SHEET_HANDLE_NAME'
|
|
43
|
+
import { SheetProvider, useSheetContext } from './SheetContext'
|
|
44
|
+
import { SheetScrollView } from './SheetScrollView'
|
|
45
|
+
import { ScrollBridge, SheetProps, SheetScopedProps } from './types'
|
|
50
46
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
export type SheetProps = ScopedProps<
|
|
54
|
-
{
|
|
55
|
-
open?: boolean
|
|
56
|
-
defaultOpen?: boolean
|
|
57
|
-
onChangeOpen?: OpenChangeHandler
|
|
58
|
-
position?: number
|
|
59
|
-
defaultPosition?: number
|
|
60
|
-
snapPoints?: number[]
|
|
61
|
-
onChangePosition?: PositionChangeHandler
|
|
62
|
-
children?: ReactNode
|
|
63
|
-
dismissOnOverlayPress?: boolean
|
|
64
|
-
dismissOnSnapToBottom?: boolean
|
|
65
|
-
animationConfig?: Animated.SpringAnimationConfig
|
|
66
|
-
disableDrag?: boolean
|
|
67
|
-
modal?: boolean
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* @see https://github.com/theKashey/react-remove-scroll#usage
|
|
71
|
-
*/
|
|
72
|
-
allowPinchZoom?: RemoveScrollProps['allowPinchZoom']
|
|
73
|
-
},
|
|
74
|
-
'Sheet'
|
|
75
|
-
>
|
|
76
|
-
|
|
77
|
-
type PositionChangeHandler = (position: number) => void
|
|
78
|
-
|
|
79
|
-
type OpenChangeHandler = ((open: boolean) => void) | React.Dispatch<React.SetStateAction<boolean>>
|
|
80
|
-
|
|
81
|
-
type ScrollBridge = {
|
|
82
|
-
enabled: boolean
|
|
83
|
-
y: number
|
|
84
|
-
scrollStartY: number
|
|
85
|
-
drag: (dy: number) => void
|
|
86
|
-
release: (state: { dy: number; vy: number }) => void
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
type SheetContextValue = Required<
|
|
90
|
-
Pick<SheetProps, 'open' | 'position' | 'snapPoints' | 'dismissOnOverlayPress'>
|
|
91
|
-
> & {
|
|
92
|
-
hidden: boolean
|
|
93
|
-
setPosition: PositionChangeHandler
|
|
94
|
-
setOpen: React.Dispatch<React.SetStateAction<boolean>>
|
|
95
|
-
allowPinchZoom: RemoveScrollProps['allowPinchZoom']
|
|
96
|
-
contentRef: React.RefObject<TamaguiElement>
|
|
97
|
-
dismissOnSnapToBottom: boolean
|
|
98
|
-
scrollBridge: ScrollBridge
|
|
99
|
-
modal: boolean
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const [createSheetContext, createSheetScope] = createContextScope(SHEET_NAME)
|
|
103
|
-
const [SheetProvider, useSheetContext] = createSheetContext<SheetContextValue>(
|
|
104
|
-
SHEET_NAME,
|
|
105
|
-
{} as any
|
|
106
|
-
)
|
|
47
|
+
export { createSheetScope } from './SheetContext'
|
|
107
48
|
|
|
108
49
|
/* -------------------------------------------------------------------------------------------------
|
|
109
50
|
* SheetHandle
|
|
@@ -114,30 +55,31 @@ export const SheetHandleFrame = styled(XStack, {
|
|
|
114
55
|
height: 10,
|
|
115
56
|
borderRadius: 100,
|
|
116
57
|
backgroundColor: '$background',
|
|
117
|
-
position: 'absolute',
|
|
118
|
-
pointerEvents: 'auto',
|
|
119
58
|
zIndex: 10,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
left: '35%',
|
|
123
|
-
right: '35%',
|
|
59
|
+
marginHorizontal: '35%',
|
|
60
|
+
marginBottom: '$2',
|
|
124
61
|
opacity: 0.5,
|
|
125
62
|
|
|
126
63
|
hoverStyle: {
|
|
127
64
|
opacity: 0.7,
|
|
128
65
|
},
|
|
129
|
-
})
|
|
130
66
|
|
|
131
|
-
|
|
67
|
+
variants: {
|
|
68
|
+
open: {
|
|
69
|
+
true: {
|
|
70
|
+
pointerEvents: 'auto',
|
|
71
|
+
},
|
|
72
|
+
false: {
|
|
73
|
+
opacity: 0,
|
|
74
|
+
pointerEvents: 'none',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
} as const,
|
|
78
|
+
})
|
|
132
79
|
|
|
133
80
|
export const SheetHandle = SheetHandleFrame.extractable(
|
|
134
81
|
({ __scopeSheet, ...props }: SheetScopedProps<XStackProps>) => {
|
|
135
82
|
const context = useSheetContext(SHEET_HANDLE_NAME, __scopeSheet)
|
|
136
|
-
|
|
137
|
-
if (context.open === false) {
|
|
138
|
-
return null
|
|
139
|
-
}
|
|
140
|
-
|
|
141
83
|
return (
|
|
142
84
|
<SheetHandleFrame
|
|
143
85
|
onPress={() => {
|
|
@@ -146,6 +88,7 @@ export const SheetHandle = SheetHandleFrame.extractable(
|
|
|
146
88
|
const nextPos = (context.position + 1) % max
|
|
147
89
|
context.setPosition(nextPos)
|
|
148
90
|
}}
|
|
91
|
+
open={context.open}
|
|
149
92
|
{...props}
|
|
150
93
|
/>
|
|
151
94
|
)
|
|
@@ -186,108 +129,17 @@ export const SheetOverlay = SheetOverlayFrame.extractable(
|
|
|
186
129
|
({ __scopeSheet, ...props }: SheetScopedProps<SheetOverlayProps>) => {
|
|
187
130
|
const context = useSheetContext(SHEET_OVERLAY_NAME, __scopeSheet)
|
|
188
131
|
return (
|
|
189
|
-
<
|
|
190
|
-
|
|
191
|
-
as={Slot}
|
|
192
|
-
allowPinchZoom={context.allowPinchZoom}
|
|
193
|
-
shards={[context.contentRef]}
|
|
194
|
-
// causes lots of bugs on touch web on site
|
|
195
|
-
removeScrollBar={false}
|
|
196
|
-
>
|
|
197
|
-
<SheetOverlayFrame
|
|
198
|
-
closed={!context.open || context.hidden}
|
|
199
|
-
{...props}
|
|
200
|
-
onPress={mergeEvent(
|
|
201
|
-
props.onPress,
|
|
202
|
-
context.dismissOnOverlayPress
|
|
203
|
-
? () => {
|
|
204
|
-
context.setOpen(false)
|
|
205
|
-
}
|
|
206
|
-
: undefined
|
|
207
|
-
)}
|
|
208
|
-
/>
|
|
209
|
-
</RemoveScroll>
|
|
210
|
-
)
|
|
211
|
-
}
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
/* -------------------------------------------------------------------------------------------------
|
|
215
|
-
* SheetScrollView
|
|
216
|
-
* -----------------------------------------------------------------------------------------------*/
|
|
217
|
-
|
|
218
|
-
const SHEET_SCROLL_VIEW_NAME = 'SheetScrollView'
|
|
219
|
-
|
|
220
|
-
export const SheetScrollView = forwardRef<ScrollView, ScrollViewProps>(
|
|
221
|
-
({ __scopeSheet, ...props }: SheetScopedProps<ScrollViewProps>, ref) => {
|
|
222
|
-
const { scrollBridge } = useSheetContext(SHEET_SCROLL_VIEW_NAME, __scopeSheet)
|
|
223
|
-
const [scrollEnabled, setScrollEnabled] = useState(true)
|
|
224
|
-
const state = useRef({
|
|
225
|
-
dy: 0,
|
|
226
|
-
// store a few recent dys to get velocity on release
|
|
227
|
-
dys: [] as number[],
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
const release = () => {
|
|
231
|
-
setScrollEnabled(true)
|
|
232
|
-
const recentDys = state.current.dys.slice(-10)
|
|
233
|
-
const dist = recentDys.length
|
|
234
|
-
? recentDys.reduce((a, b, i) => a + b - (recentDys[i - 1] ?? recentDys[0]), 0)
|
|
235
|
-
: 0
|
|
236
|
-
const avgDy = dist / recentDys.length
|
|
237
|
-
const vy = avgDy * 0.075
|
|
238
|
-
state.current.dys = []
|
|
239
|
-
scrollBridge.release({
|
|
240
|
-
dy: state.current.dy,
|
|
241
|
-
vy,
|
|
242
|
-
})
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return (
|
|
246
|
-
<ScrollView
|
|
247
|
-
ref={ref}
|
|
248
|
-
scrollEventThrottle={16} // todo release we can just grab the last dY and estimate vY using a sample of last dYs
|
|
132
|
+
<SheetOverlayFrame
|
|
133
|
+
closed={!context.open || context.hidden}
|
|
249
134
|
{...props}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
scrollBridge.scrollStartY = -1
|
|
258
|
-
}
|
|
259
|
-
}
|
|
135
|
+
onPress={mergeEvent(
|
|
136
|
+
props.onPress,
|
|
137
|
+
context.dismissOnOverlayPress
|
|
138
|
+
? () => {
|
|
139
|
+
context.setOpen(false)
|
|
140
|
+
}
|
|
141
|
+
: undefined
|
|
260
142
|
)}
|
|
261
|
-
onResponderMove={composeEventHandlers(props.onResponderMove, (e) => {
|
|
262
|
-
const { pageY } = e.nativeEvent
|
|
263
|
-
if (scrollBridge.y <= 0) {
|
|
264
|
-
if (scrollBridge.scrollStartY === -1) {
|
|
265
|
-
scrollBridge.scrollStartY = pageY
|
|
266
|
-
}
|
|
267
|
-
const dy = pageY - scrollBridge.scrollStartY
|
|
268
|
-
if (dy <= 0) {
|
|
269
|
-
setScrollEnabled(true)
|
|
270
|
-
return
|
|
271
|
-
}
|
|
272
|
-
setScrollEnabled(false)
|
|
273
|
-
scrollBridge.drag(dy)
|
|
274
|
-
state.current.dy = dy
|
|
275
|
-
state.current.dys.push(dy)
|
|
276
|
-
// only do every so often, cut down to 10 again
|
|
277
|
-
if (state.current.dys.length > 100) {
|
|
278
|
-
state.current.dys = state.current.dys.slice(-10)
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
})}
|
|
282
|
-
onResponderReject={composeEventHandlers(props.onResponderReject, release)}
|
|
283
|
-
onResponderTerminate={composeEventHandlers(props.onResponderTerminate, release)}
|
|
284
|
-
onResponderRelease={composeEventHandlers(props.onResponderRelease, release)}
|
|
285
|
-
style={[
|
|
286
|
-
{
|
|
287
|
-
flex: 1,
|
|
288
|
-
},
|
|
289
|
-
props.style,
|
|
290
|
-
]}
|
|
291
143
|
/>
|
|
292
144
|
)
|
|
293
145
|
}
|
|
@@ -343,7 +195,7 @@ export const Sheet = withStaticProperties(
|
|
|
343
195
|
dismissOnSnapToBottom = false,
|
|
344
196
|
disableDrag: disableDragProp,
|
|
345
197
|
modal = false,
|
|
346
|
-
|
|
198
|
+
handleDisableScroll = true,
|
|
347
199
|
} = props
|
|
348
200
|
|
|
349
201
|
if (process.env.NODE_ENV === 'development') {
|
|
@@ -363,9 +215,12 @@ export const Sheet = withStaticProperties(
|
|
|
363
215
|
const scrollBridge = useConstant<ScrollBridge>(() => ({
|
|
364
216
|
enabled: false,
|
|
365
217
|
y: 0,
|
|
218
|
+
paneY: 0,
|
|
219
|
+
paneMinY: 0,
|
|
366
220
|
scrollStartY: -1,
|
|
367
221
|
drag: () => {},
|
|
368
222
|
release: () => {},
|
|
223
|
+
scrollLock: false,
|
|
369
224
|
}))
|
|
370
225
|
|
|
371
226
|
const onChangeOpenInternal = (val: boolean) => {
|
|
@@ -431,6 +286,10 @@ export const Sheet = withStaticProperties(
|
|
|
431
286
|
function stopSpring() {
|
|
432
287
|
spring.current?.stop()
|
|
433
288
|
spring.current = null
|
|
289
|
+
if (scrollBridge.onFinishAnimate) {
|
|
290
|
+
scrollBridge.onFinishAnimate()
|
|
291
|
+
scrollBridge.onFinishAnimate = undefined
|
|
292
|
+
}
|
|
434
293
|
}
|
|
435
294
|
|
|
436
295
|
// open must set position
|
|
@@ -491,6 +350,7 @@ export const Sheet = withStaticProperties(
|
|
|
491
350
|
useEffect(() => {
|
|
492
351
|
positionValue.current!.addListener(({ value }) => {
|
|
493
352
|
at.current = value
|
|
353
|
+
scrollBridge.paneY = value
|
|
494
354
|
})
|
|
495
355
|
return () => {
|
|
496
356
|
positionValue.current!.removeAllListeners()
|
|
@@ -505,6 +365,7 @@ export const Sheet = withStaticProperties(
|
|
|
505
365
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
506
366
|
const pos = positionValue.current!
|
|
507
367
|
const minY = positions[0]
|
|
368
|
+
scrollBridge.paneMinY = minY
|
|
508
369
|
let startY = at.current
|
|
509
370
|
|
|
510
371
|
function makeUnselectable(val: boolean) {
|
|
@@ -516,14 +377,14 @@ export const Sheet = withStaticProperties(
|
|
|
516
377
|
}
|
|
517
378
|
}
|
|
518
379
|
|
|
519
|
-
const release = ({ vy,
|
|
380
|
+
const release = ({ vy, dragAt }: { dragAt: number; vy: number }) => {
|
|
520
381
|
isExternalDrag = false
|
|
521
382
|
previouslyScrolling = false
|
|
522
383
|
makeUnselectable(false)
|
|
523
|
-
const at =
|
|
384
|
+
const at = dragAt + startY
|
|
524
385
|
// seems liky vy goes up to about 4 at the very most (+ is down, - is up)
|
|
525
386
|
// lets base our multiplier on the total layout height
|
|
526
|
-
const end = at + frameSize * vy * 0.
|
|
387
|
+
const end = at + frameSize * vy * 0.2
|
|
527
388
|
let closestPoint = 0
|
|
528
389
|
let dist = Infinity
|
|
529
390
|
for (let i = 0; i < positions.length; i++) {
|
|
@@ -540,25 +401,28 @@ export const Sheet = withStaticProperties(
|
|
|
540
401
|
}
|
|
541
402
|
|
|
542
403
|
const finish = (_e: GestureResponderEvent, state: PanResponderGestureState) => {
|
|
543
|
-
release(
|
|
404
|
+
release({
|
|
405
|
+
vy: state.vy,
|
|
406
|
+
dragAt: state.dy,
|
|
407
|
+
})
|
|
544
408
|
}
|
|
545
409
|
|
|
546
410
|
let previouslyScrolling = false
|
|
547
411
|
|
|
548
412
|
const onMoveShouldSet = (_e: GestureResponderEvent, { dy }: PanResponderGestureState) => {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
}
|
|
554
|
-
if (scrollBridge.y === 0 && dy < 0) {
|
|
555
|
-
return false
|
|
556
|
-
}
|
|
413
|
+
const isScrolled = scrollBridge.y !== 0
|
|
414
|
+
if (isScrolled) {
|
|
415
|
+
previouslyScrolling = true
|
|
416
|
+
return false
|
|
557
417
|
}
|
|
558
418
|
if (previouslyScrolling) {
|
|
559
419
|
previouslyScrolling = false
|
|
560
420
|
return true
|
|
561
421
|
}
|
|
422
|
+
const isDraggingUp = dy < 0
|
|
423
|
+
if (!isScrolled && isDraggingUp) {
|
|
424
|
+
return false
|
|
425
|
+
}
|
|
562
426
|
// we could do some detection of other touchables and cancel here..
|
|
563
427
|
return Math.abs(dy) > 8
|
|
564
428
|
}
|
|
@@ -571,27 +435,35 @@ export const Sheet = withStaticProperties(
|
|
|
571
435
|
|
|
572
436
|
let isExternalDrag = false
|
|
573
437
|
|
|
438
|
+
function setPositionValue(next: number) {
|
|
439
|
+
// useful to debug any "jumping"
|
|
440
|
+
// if (process.env.NODE_ENV === 'development') {
|
|
441
|
+
// const moveAmt = Math.abs(pos['_value'] - next)
|
|
442
|
+
// if (moveAmt > 50) {
|
|
443
|
+
// console.warn('!!!!!!!!!!!!jump!', moveAmt)
|
|
444
|
+
// // debugger
|
|
445
|
+
// }
|
|
446
|
+
// }
|
|
447
|
+
pos.setValue(next)
|
|
448
|
+
}
|
|
449
|
+
|
|
574
450
|
scrollBridge.drag = (dy) => {
|
|
575
451
|
if (!isExternalDrag) {
|
|
576
452
|
isExternalDrag = true
|
|
577
453
|
grant()
|
|
578
454
|
}
|
|
579
455
|
const to = dy + startY
|
|
580
|
-
|
|
456
|
+
setPositionValue(resisted(to, minY))
|
|
581
457
|
}
|
|
582
458
|
|
|
583
459
|
scrollBridge.release = release
|
|
584
460
|
|
|
585
461
|
return PanResponder.create({
|
|
586
|
-
onMoveShouldSetPanResponder:
|
|
587
|
-
const res = onMoveShouldSet(...args)
|
|
588
|
-
// console.log('res', res, scrollBridge.y)
|
|
589
|
-
return res
|
|
590
|
-
},
|
|
462
|
+
onMoveShouldSetPanResponder: onMoveShouldSet,
|
|
591
463
|
onPanResponderGrant: grant,
|
|
592
464
|
onPanResponderMove: (_e, { dy }) => {
|
|
593
465
|
const to = dy + startY
|
|
594
|
-
|
|
466
|
+
setPositionValue(resisted(to, minY))
|
|
595
467
|
},
|
|
596
468
|
onPanResponderEnd: finish,
|
|
597
469
|
onPanResponderTerminate: finish,
|
|
@@ -640,7 +512,6 @@ export const Sheet = withStaticProperties(
|
|
|
640
512
|
contentRef={contentRef}
|
|
641
513
|
dismissOnOverlayPress={dismissOnOverlayPress}
|
|
642
514
|
dismissOnSnapToBottom={dismissOnSnapToBottom}
|
|
643
|
-
allowPinchZoom={allowPinchZoom}
|
|
644
515
|
open={open}
|
|
645
516
|
hidden={isHidden}
|
|
646
517
|
scope={__scopeSheet}
|
|
@@ -651,8 +522,7 @@ export const Sheet = withStaticProperties(
|
|
|
651
522
|
scrollBridge={scrollBridge}
|
|
652
523
|
>
|
|
653
524
|
{isResizing ? null : overlayComponent}
|
|
654
|
-
|
|
655
|
-
{isHidden ? null : handleComponent}
|
|
525
|
+
|
|
656
526
|
<Animated.View
|
|
657
527
|
ref={ref}
|
|
658
528
|
{...panResponder?.panHandlers}
|
|
@@ -673,7 +543,18 @@ export const Sheet = withStaticProperties(
|
|
|
673
543
|
transform: [{ translateY }],
|
|
674
544
|
}}
|
|
675
545
|
>
|
|
546
|
+
{handleComponent}
|
|
547
|
+
|
|
548
|
+
{/* <RemoveScroll
|
|
549
|
+
enabled={open && modal && handleDisableScroll}
|
|
550
|
+
as={Slot}
|
|
551
|
+
allowPinchZoom
|
|
552
|
+
shards={[contentRef]}
|
|
553
|
+
// causes lots of bugs on touch web on site
|
|
554
|
+
removeScrollBar={false}
|
|
555
|
+
> */}
|
|
676
556
|
{isResizing ? null : frameComponent}
|
|
557
|
+
{/* </RemoveScroll> */}
|
|
677
558
|
</Animated.View>
|
|
678
559
|
</SheetProvider>
|
|
679
560
|
)
|
|
@@ -752,5 +633,3 @@ export const SheetController = ({
|
|
|
752
633
|
<SheetControllerContext.Provider value={memoValue}>{children}</SheetControllerContext.Provider>
|
|
753
634
|
)
|
|
754
635
|
}
|
|
755
|
-
|
|
756
|
-
export { createSheetScope }
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { TamaguiElement } from '@tamagui/core'
|
|
2
|
+
import { createContextScope } from '@tamagui/create-context'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
|
|
5
|
+
import { SHEET_NAME } from './SHEET_HANDLE_NAME'
|
|
6
|
+
import { PositionChangeHandler, ScrollBridge, SheetProps } from './types'
|
|
7
|
+
|
|
8
|
+
type SheetContextValue = Required<
|
|
9
|
+
Pick<SheetProps, 'open' | 'position' | 'snapPoints' | 'dismissOnOverlayPress'>
|
|
10
|
+
> & {
|
|
11
|
+
hidden: boolean
|
|
12
|
+
setPosition: PositionChangeHandler
|
|
13
|
+
setOpen: React.Dispatch<React.SetStateAction<boolean>>
|
|
14
|
+
contentRef: React.RefObject<TamaguiElement>
|
|
15
|
+
dismissOnSnapToBottom: boolean
|
|
16
|
+
scrollBridge: ScrollBridge
|
|
17
|
+
modal: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const [createSheetContext, createSheetScope] = createContextScope(SHEET_NAME)
|
|
21
|
+
|
|
22
|
+
export const [SheetProvider, useSheetContext] = createSheetContext<SheetContextValue>(
|
|
23
|
+
SHEET_NAME,
|
|
24
|
+
{} as any
|
|
25
|
+
)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import React, { forwardRef, useRef, useState } from 'react'
|
|
2
|
+
import { ScrollView, ScrollViewProps } from 'react-native'
|
|
3
|
+
|
|
4
|
+
import { useSheetContext } from './SheetContext'
|
|
5
|
+
import { SheetScopedProps } from './types'
|
|
6
|
+
|
|
7
|
+
/* -------------------------------------------------------------------------------------------------
|
|
8
|
+
* SheetScrollView
|
|
9
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
10
|
+
|
|
11
|
+
const SHEET_SCROLL_VIEW_NAME = 'SheetScrollView'
|
|
12
|
+
|
|
13
|
+
export const SheetScrollView = forwardRef<ScrollView, ScrollViewProps>(
|
|
14
|
+
({ __scopeSheet, ...props }: SheetScopedProps<ScrollViewProps>, ref) => {
|
|
15
|
+
const { scrollBridge } = useSheetContext(SHEET_SCROLL_VIEW_NAME, __scopeSheet)
|
|
16
|
+
const [scrollEnabled, setScrollEnabled] = useState(true)
|
|
17
|
+
const state = useRef({
|
|
18
|
+
lastPageY: 0,
|
|
19
|
+
dragAt: 0,
|
|
20
|
+
dys: [] as number[], // store a few recent dys to get velocity on release
|
|
21
|
+
isScrolling: false,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const release = () => {
|
|
25
|
+
scrollBridge.scrollStartY = -1
|
|
26
|
+
state.current.isScrolling = false
|
|
27
|
+
setScrollEnabled(true)
|
|
28
|
+
let vy = 0
|
|
29
|
+
if (state.current.dys.length) {
|
|
30
|
+
const recentDys = state.current.dys.slice(-10)
|
|
31
|
+
const dist = recentDys.length ? recentDys.reduce((a, b) => a + b, 0) : 0
|
|
32
|
+
const avgDy = dist / recentDys.length
|
|
33
|
+
vy = avgDy * 0.04
|
|
34
|
+
console.table([{ vy, avgDy, dist, recentDys }])
|
|
35
|
+
}
|
|
36
|
+
state.current.dys = []
|
|
37
|
+
scrollBridge.release({
|
|
38
|
+
dragAt: state.current.dragAt,
|
|
39
|
+
vy,
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<ScrollView
|
|
45
|
+
ref={ref}
|
|
46
|
+
scrollEventThrottle={8}
|
|
47
|
+
scrollEnabled={scrollEnabled}
|
|
48
|
+
onScroll={(e) => {
|
|
49
|
+
const { y } = e.nativeEvent.contentOffset
|
|
50
|
+
scrollBridge.y = y
|
|
51
|
+
if (y > 0) {
|
|
52
|
+
scrollBridge.scrollStartY = -1
|
|
53
|
+
}
|
|
54
|
+
}}
|
|
55
|
+
onStartShouldSetResponder={() => {
|
|
56
|
+
scrollBridge.scrollStartY = -1
|
|
57
|
+
return true
|
|
58
|
+
}}
|
|
59
|
+
onMoveShouldSetResponder={() => true}
|
|
60
|
+
onResponderMove={(e) => {
|
|
61
|
+
const { pageY } = e.nativeEvent
|
|
62
|
+
|
|
63
|
+
if (state.current.isScrolling) {
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (scrollBridge.scrollStartY === -1) {
|
|
68
|
+
scrollBridge.scrollStartY = pageY
|
|
69
|
+
state.current.lastPageY = pageY
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const dragAt = pageY - scrollBridge.scrollStartY
|
|
73
|
+
const dy = pageY - state.current.lastPageY
|
|
74
|
+
state.current.lastPageY = pageY // after dy
|
|
75
|
+
// const isAboveStart = dragAt <= 0
|
|
76
|
+
// const isDraggingDown = dy > 0
|
|
77
|
+
// const isScrollAtTop = scrollBridge.y <= 0
|
|
78
|
+
const isDraggingUp = dy < 0
|
|
79
|
+
const isPaneAtTop = scrollBridge.paneY <= scrollBridge.paneMinY
|
|
80
|
+
|
|
81
|
+
if ((dy === 0 || isDraggingUp) && isPaneAtTop) {
|
|
82
|
+
state.current.isScrolling = true
|
|
83
|
+
setScrollEnabled(true)
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
setScrollEnabled(false)
|
|
88
|
+
scrollBridge.drag(dragAt)
|
|
89
|
+
state.current.dragAt = dragAt
|
|
90
|
+
state.current.dys.push(dy)
|
|
91
|
+
// only do every so often, cut down to 10 again
|
|
92
|
+
if (state.current.dys.length > 100) {
|
|
93
|
+
state.current.dys = state.current.dys.slice(-10)
|
|
94
|
+
}
|
|
95
|
+
}}
|
|
96
|
+
onResponderRelease={release}
|
|
97
|
+
// onResponderEnd={release}
|
|
98
|
+
// onResponderTerminate={release}
|
|
99
|
+
{...props}
|
|
100
|
+
style={[
|
|
101
|
+
{
|
|
102
|
+
flex: 1,
|
|
103
|
+
},
|
|
104
|
+
props.style,
|
|
105
|
+
]}
|
|
106
|
+
/>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
)
|
package/src/types.tsx
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ScopedProps } from '@tamagui/create-context'
|
|
2
|
+
import { RemoveScroll } from '@tamagui/remove-scroll'
|
|
3
|
+
import React, { ReactNode } from 'react'
|
|
4
|
+
import { Animated } from 'react-native'
|
|
5
|
+
|
|
6
|
+
export type SheetProps = ScopedProps<
|
|
7
|
+
{
|
|
8
|
+
open?: boolean
|
|
9
|
+
defaultOpen?: boolean
|
|
10
|
+
onChangeOpen?: OpenChangeHandler
|
|
11
|
+
position?: number
|
|
12
|
+
defaultPosition?: number
|
|
13
|
+
snapPoints?: number[]
|
|
14
|
+
onChangePosition?: PositionChangeHandler
|
|
15
|
+
children?: ReactNode
|
|
16
|
+
dismissOnOverlayPress?: boolean
|
|
17
|
+
dismissOnSnapToBottom?: boolean
|
|
18
|
+
animationConfig?: Animated.SpringAnimationConfig
|
|
19
|
+
handleDisableScroll?: boolean
|
|
20
|
+
disableDrag?: boolean
|
|
21
|
+
modal?: boolean
|
|
22
|
+
},
|
|
23
|
+
'Sheet'
|
|
24
|
+
>
|
|
25
|
+
|
|
26
|
+
export type PositionChangeHandler = (position: number) => void
|
|
27
|
+
|
|
28
|
+
type OpenChangeHandler = ((open: boolean) => void) | React.Dispatch<React.SetStateAction<boolean>>
|
|
29
|
+
|
|
30
|
+
export type RemoveScrollProps = React.ComponentProps<typeof RemoveScroll>
|
|
31
|
+
|
|
32
|
+
export type SheetScopedProps<A> = ScopedProps<A, 'Sheet'>
|
|
33
|
+
|
|
34
|
+
export type ScrollBridge = {
|
|
35
|
+
enabled: boolean
|
|
36
|
+
y: number
|
|
37
|
+
paneY: number
|
|
38
|
+
paneMinY: number
|
|
39
|
+
scrollStartY: number
|
|
40
|
+
drag: (dy: number) => void
|
|
41
|
+
release: (state: { dragAt: number; vy: number }) => void
|
|
42
|
+
scrollLock: boolean
|
|
43
|
+
onFinishAnimate?: () => void
|
|
44
|
+
}
|