@tamagui/sheet 1.0.1-beta.200 → 1.0.1-beta.201
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/Sheet.js +360 -345
- package/dist/cjs/Sheet.js.map +3 -3
- package/dist/esm/Sheet.js +360 -345
- package/dist/esm/Sheet.js.map +3 -3
- package/dist/jsx/Sheet.js +316 -304
- package/dist/jsx/Sheet.js.map +3 -3
- package/package.json +11 -11
- package/src/Sheet.tsx +394 -379
- package/types/Sheet.d.ts +2 -5
package/src/Sheet.tsx
CHANGED
|
@@ -183,432 +183,447 @@ const ParentSheetContext = createContext({
|
|
|
183
183
|
zIndex: 40,
|
|
184
184
|
})
|
|
185
185
|
|
|
186
|
+
const useSheetContoller = () => {
|
|
187
|
+
const controller = useContext(SheetControllerContext)
|
|
188
|
+
const isHidden = controller?.hidden || false
|
|
189
|
+
const isShowingNonSheet = isHidden && controller?.open
|
|
190
|
+
return {
|
|
191
|
+
controller,
|
|
192
|
+
isHidden,
|
|
193
|
+
isShowingNonSheet,
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
186
197
|
export const Sheet = withStaticProperties(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
defaultPosition,
|
|
201
|
-
dismissOnOverlayPress = true,
|
|
202
|
-
animationConfig,
|
|
203
|
-
dismissOnSnapToBottom = false,
|
|
204
|
-
disableDrag: disableDragProp,
|
|
205
|
-
modal = false,
|
|
206
|
-
handleDisableScroll = true,
|
|
207
|
-
zIndex = parentSheet.zIndex + 1,
|
|
208
|
-
} = props
|
|
209
|
-
|
|
210
|
-
if (process.env.NODE_ENV === 'development') {
|
|
211
|
-
if (snapPointsProp.some((p) => p < 0 || p > 100)) {
|
|
212
|
-
// eslint-disable-next-line no-console
|
|
213
|
-
console.warn(
|
|
214
|
-
`⚠️ Invalid snapPoint given, snapPoints must be between 0 and 100, equal to percent height of frame`
|
|
215
|
-
)
|
|
216
|
-
}
|
|
217
|
-
}
|
|
198
|
+
forwardRef<View, SheetProps>(function Sheet(props, ref) {
|
|
199
|
+
const { isShowingNonSheet } = useSheetContoller()
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Performance is sensitive here so avoid all the hooks below with this
|
|
203
|
+
*/
|
|
204
|
+
if (isShowingNonSheet) {
|
|
205
|
+
return null
|
|
206
|
+
}
|
|
207
|
+
return <SheetImplementation ref={ref} {...props} />
|
|
208
|
+
}),
|
|
209
|
+
sheetComponents
|
|
210
|
+
)
|
|
218
211
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
212
|
+
const SheetImplementation = themeable(
|
|
213
|
+
forwardRef<View, SheetProps>(function SheetImplementation(props, ref) {
|
|
214
|
+
const parentSheet = useContext(ParentSheetContext)
|
|
215
|
+
const { isHidden, controller } = useSheetContoller()
|
|
216
|
+
|
|
217
|
+
const {
|
|
218
|
+
__scopeSheet,
|
|
219
|
+
snapPoints: snapPointsProp = [80],
|
|
220
|
+
open: openProp,
|
|
221
|
+
defaultOpen,
|
|
222
|
+
children: childrenProp,
|
|
223
|
+
position: positionProp,
|
|
224
|
+
onPositionChange,
|
|
225
|
+
onOpenChange,
|
|
226
|
+
defaultPosition,
|
|
227
|
+
dismissOnOverlayPress = true,
|
|
228
|
+
animationConfig,
|
|
229
|
+
dismissOnSnapToBottom = false,
|
|
230
|
+
disableDrag: disableDragProp,
|
|
231
|
+
modal = false,
|
|
232
|
+
handleDisableScroll = true,
|
|
233
|
+
zIndex = parentSheet.zIndex + 1,
|
|
234
|
+
} = props
|
|
235
|
+
|
|
236
|
+
if (process.env.NODE_ENV === 'development') {
|
|
237
|
+
if (snapPointsProp.some((p) => p < 0 || p > 100)) {
|
|
238
|
+
// eslint-disable-next-line no-console
|
|
239
|
+
console.warn(
|
|
240
|
+
`⚠️ Invalid snapPoint given, snapPoints must be between 0 and 100, equal to percent height of frame`
|
|
241
|
+
)
|
|
222
242
|
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const driver = getAnimationDriver()
|
|
246
|
+
if (!driver) {
|
|
247
|
+
throw new Error(`Must set animations in tamagui.config.ts`)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const disableDrag = disableDragProp ?? controller?.disableDrag
|
|
251
|
+
const themeName = useThemeName()
|
|
252
|
+
const contentRef = React.useRef<TamaguiElement>(null)
|
|
253
|
+
const scrollBridge = useConstant<ScrollBridge>(() => ({
|
|
254
|
+
enabled: false,
|
|
255
|
+
y: 0,
|
|
256
|
+
paneY: 0,
|
|
257
|
+
paneMinY: 0,
|
|
258
|
+
scrollStartY: -1,
|
|
259
|
+
drag: () => {},
|
|
260
|
+
release: () => {},
|
|
261
|
+
scrollLock: false,
|
|
262
|
+
}))
|
|
263
|
+
|
|
264
|
+
const onOpenChangeInternal = (val: boolean) => {
|
|
265
|
+
controller?.onOpenChange?.(val)
|
|
266
|
+
onOpenChange?.(val)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const [open, setOpen] = useControllableState({
|
|
270
|
+
prop: controller?.open ?? openProp,
|
|
271
|
+
defaultProp: defaultOpen || true,
|
|
272
|
+
onChange: onOpenChangeInternal,
|
|
273
|
+
strategy: 'most-recent-wins',
|
|
274
|
+
})
|
|
223
275
|
|
|
224
|
-
|
|
225
|
-
const controller = useContext(SheetControllerContext)
|
|
226
|
-
const isHidden = controller?.hidden || false
|
|
227
|
-
const disableDrag = disableDragProp ?? controller?.disableDrag
|
|
228
|
-
const themeName = useThemeName()
|
|
229
|
-
const contentRef = React.useRef<TamaguiElement>(null)
|
|
230
|
-
const scrollBridge = useConstant<ScrollBridge>(() => ({
|
|
231
|
-
enabled: false,
|
|
232
|
-
y: 0,
|
|
233
|
-
paneY: 0,
|
|
234
|
-
paneMinY: 0,
|
|
235
|
-
scrollStartY: -1,
|
|
236
|
-
drag: () => {},
|
|
237
|
-
release: () => {},
|
|
238
|
-
scrollLock: false,
|
|
239
|
-
}))
|
|
240
|
-
|
|
241
|
-
const onOpenChangeInternal = (val: boolean) => {
|
|
242
|
-
controller?.onOpenChange?.(val)
|
|
243
|
-
onOpenChange?.(val)
|
|
244
|
-
}
|
|
276
|
+
const [frameSize, setFrameSize] = useState<number>(0)
|
|
245
277
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
})
|
|
278
|
+
const snapPoints = useMemo(
|
|
279
|
+
() => (dismissOnSnapToBottom ? [...snapPointsProp, 0] : snapPointsProp),
|
|
280
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
281
|
+
[JSON.stringify(snapPointsProp), dismissOnSnapToBottom]
|
|
282
|
+
)
|
|
252
283
|
|
|
253
|
-
|
|
284
|
+
// lets set -1 to be always the "open = false" position
|
|
285
|
+
const [position_, setPosition_] = useControllableState({
|
|
286
|
+
prop: positionProp,
|
|
287
|
+
defaultProp: defaultPosition || (open ? 0 : -1),
|
|
288
|
+
onChange: onPositionChange,
|
|
289
|
+
strategy: 'most-recent-wins',
|
|
290
|
+
})
|
|
291
|
+
const position = open === false ? -1 : position_
|
|
292
|
+
|
|
293
|
+
// reset position to fully open on re-open after dismissOnSnapToBottom
|
|
294
|
+
if (open && dismissOnSnapToBottom && position === snapPoints.length - 1) {
|
|
295
|
+
setPosition_(0)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const setPosition = useCallback(
|
|
299
|
+
(next: number) => {
|
|
300
|
+
// close on dismissOnSnapToBottom (and set position so it animates)
|
|
301
|
+
if (dismissOnSnapToBottom && next === snapPoints.length - 1) {
|
|
302
|
+
setOpen(false)
|
|
303
|
+
} else {
|
|
304
|
+
setPosition_(next)
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
[dismissOnSnapToBottom, snapPoints.length, setPosition_, setOpen]
|
|
308
|
+
)
|
|
254
309
|
|
|
255
|
-
|
|
256
|
-
() => (dismissOnSnapToBottom ? [...snapPointsProp, 0] : snapPointsProp),
|
|
257
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
258
|
-
[JSON.stringify(snapPointsProp), dismissOnSnapToBottom]
|
|
259
|
-
)
|
|
310
|
+
const animatedNumber = driver.useAnimatedNumber(HIDDEN_SIZE)
|
|
260
311
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
})
|
|
268
|
-
const position = open === false ? -1 : position_
|
|
312
|
+
// native only fix
|
|
313
|
+
const at = useRef(0)
|
|
314
|
+
driver.useAnimatedNumberReaction(animatedNumber, (value) => {
|
|
315
|
+
at.current = value
|
|
316
|
+
scrollBridge.paneY = value
|
|
317
|
+
})
|
|
269
318
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
319
|
+
const [isResizing, setIsResizing] = useState(true)
|
|
320
|
+
useIsomorphicLayoutEffect(() => {
|
|
321
|
+
if (!isResizing) {
|
|
322
|
+
setIsResizing(true)
|
|
273
323
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}
|
|
283
|
-
},
|
|
284
|
-
[dismissOnSnapToBottom, snapPoints.length, setPosition_, setOpen]
|
|
285
|
-
)
|
|
286
|
-
|
|
287
|
-
const animatedNumber = driver.useAnimatedNumber(HIDDEN_SIZE)
|
|
288
|
-
|
|
289
|
-
// native only fix
|
|
290
|
-
const at = useRef(0)
|
|
291
|
-
driver.useAnimatedNumberReaction(animatedNumber, (value) => {
|
|
292
|
-
at.current = value
|
|
293
|
-
scrollBridge.paneY = value
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
const [isResizing, setIsResizing] = useState(true)
|
|
297
|
-
useIsomorphicLayoutEffect(() => {
|
|
298
|
-
if (!isResizing) {
|
|
299
|
-
setIsResizing(true)
|
|
300
|
-
}
|
|
301
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
302
|
-
}, [modal])
|
|
303
|
-
|
|
304
|
-
function stopSpring() {
|
|
305
|
-
animatedNumber.stop()
|
|
306
|
-
if (scrollBridge.onFinishAnimate) {
|
|
307
|
-
scrollBridge.onFinishAnimate()
|
|
308
|
-
scrollBridge.onFinishAnimate = undefined
|
|
309
|
-
}
|
|
324
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
325
|
+
}, [modal])
|
|
326
|
+
|
|
327
|
+
function stopSpring() {
|
|
328
|
+
animatedNumber.stop()
|
|
329
|
+
if (scrollBridge.onFinishAnimate) {
|
|
330
|
+
scrollBridge.onFinishAnimate()
|
|
331
|
+
scrollBridge.onFinishAnimate = undefined
|
|
310
332
|
}
|
|
333
|
+
}
|
|
311
334
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
335
|
+
// open must set position
|
|
336
|
+
const shouldSetPositionOpen = open && position < 0
|
|
337
|
+
useEffect(() => {
|
|
338
|
+
if (shouldSetPositionOpen) {
|
|
339
|
+
setPosition(0)
|
|
340
|
+
}
|
|
341
|
+
}, [setPosition, shouldSetPositionOpen])
|
|
319
342
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
343
|
+
const positions = useMemo(
|
|
344
|
+
() => snapPoints.map((point) => getPercentSize(point, frameSize)),
|
|
345
|
+
[frameSize, snapPoints]
|
|
346
|
+
)
|
|
324
347
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
338
|
-
animatedNumber.setValue(toValue, {
|
|
339
|
-
type: 'timing',
|
|
340
|
-
duration: 0,
|
|
341
|
-
})
|
|
342
|
-
at.current = toValue
|
|
343
|
-
return
|
|
348
|
+
const animateTo = useEvent((position: number) => {
|
|
349
|
+
const current = animatedNumber.getValue()
|
|
350
|
+
if (isHidden && open) return
|
|
351
|
+
if (!current) return
|
|
352
|
+
if (frameSize === 0) return
|
|
353
|
+
const hiddenValue = frameSize === 0 ? HIDDEN_SIZE : frameSize
|
|
354
|
+
const toValue = isHidden || position === -1 ? hiddenValue : positions[position]
|
|
355
|
+
if (at.current === toValue) return
|
|
356
|
+
stopSpring()
|
|
357
|
+
if (isHidden || isResizing) {
|
|
358
|
+
if (isResizing) {
|
|
359
|
+
setIsResizing(false)
|
|
344
360
|
}
|
|
345
|
-
// dont bounce on initial measure to bottom
|
|
346
|
-
const overshootClamping = at.current === HIDDEN_SIZE
|
|
347
361
|
animatedNumber.setValue(toValue, {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
overshootClamping,
|
|
362
|
+
type: 'timing',
|
|
363
|
+
duration: 0,
|
|
351
364
|
})
|
|
365
|
+
at.current = toValue
|
|
366
|
+
return
|
|
367
|
+
}
|
|
368
|
+
// dont bounce on initial measure to bottom
|
|
369
|
+
const overshootClamping = at.current === HIDDEN_SIZE
|
|
370
|
+
animatedNumber.setValue(toValue, {
|
|
371
|
+
...animationConfig,
|
|
372
|
+
type: 'spring',
|
|
373
|
+
overshootClamping,
|
|
352
374
|
})
|
|
375
|
+
})
|
|
353
376
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const panResponder = useMemo(
|
|
359
|
-
() => {
|
|
360
|
-
if (disableDrag) return
|
|
361
|
-
if (!frameSize) return
|
|
362
|
-
|
|
363
|
-
const minY = positions[0]
|
|
364
|
-
scrollBridge.paneMinY = minY
|
|
365
|
-
let startY = at.current
|
|
366
|
-
|
|
367
|
-
function makeUnselectable(val: boolean) {
|
|
368
|
-
if (!selectionStyleSheet) return
|
|
369
|
-
if (!val) {
|
|
370
|
-
selectionStyleSheet.innerText = ``
|
|
371
|
-
} else {
|
|
372
|
-
selectionStyleSheet.innerText = `:root * { user-select: none !important; -webkit-user-select: none !important; }`
|
|
373
|
-
}
|
|
374
|
-
}
|
|
377
|
+
useIsomorphicLayoutEffect(() => {
|
|
378
|
+
animateTo(position)
|
|
379
|
+
}, [isHidden, frameSize, position, animateTo])
|
|
375
380
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const at = dragAt + startY
|
|
381
|
-
// seems liky vy goes up to about 4 at the very most (+ is down, - is up)
|
|
382
|
-
// lets base our multiplier on the total layout height
|
|
383
|
-
const end = at + frameSize * vy * 0.2
|
|
384
|
-
let closestPoint = 0
|
|
385
|
-
let dist = Infinity
|
|
386
|
-
for (let i = 0; i < positions.length; i++) {
|
|
387
|
-
const position = positions[i]
|
|
388
|
-
const curDist = end > position ? end - position : position - end
|
|
389
|
-
if (curDist < dist) {
|
|
390
|
-
dist = curDist
|
|
391
|
-
closestPoint = i
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
// have to call both because state may not change but need to snap back
|
|
395
|
-
setPosition(closestPoint)
|
|
396
|
-
animateTo(closestPoint)
|
|
397
|
-
}
|
|
381
|
+
const panResponder = useMemo(
|
|
382
|
+
() => {
|
|
383
|
+
if (disableDrag) return
|
|
384
|
+
if (!frameSize) return
|
|
398
385
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
dragAt: state.dy,
|
|
403
|
-
})
|
|
404
|
-
}
|
|
386
|
+
const minY = positions[0]
|
|
387
|
+
scrollBridge.paneMinY = minY
|
|
388
|
+
let startY = at.current
|
|
405
389
|
|
|
406
|
-
|
|
390
|
+
function makeUnselectable(val: boolean) {
|
|
391
|
+
if (!selectionStyleSheet) return
|
|
392
|
+
if (!val) {
|
|
393
|
+
selectionStyleSheet.innerText = ``
|
|
394
|
+
} else {
|
|
395
|
+
selectionStyleSheet.innerText = `:root * { user-select: none !important; -webkit-user-select: none !important; }`
|
|
396
|
+
}
|
|
397
|
+
}
|
|
407
398
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
if (
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
}
|
|
399
|
+
const release = ({ vy, dragAt }: { dragAt: number; vy: number }) => {
|
|
400
|
+
isExternalDrag = false
|
|
401
|
+
previouslyScrolling = false
|
|
402
|
+
makeUnselectable(false)
|
|
403
|
+
const at = dragAt + startY
|
|
404
|
+
// seems liky vy goes up to about 4 at the very most (+ is down, - is up)
|
|
405
|
+
// lets base our multiplier on the total layout height
|
|
406
|
+
const end = at + frameSize * vy * 0.2
|
|
407
|
+
let closestPoint = 0
|
|
408
|
+
let dist = Infinity
|
|
409
|
+
for (let i = 0; i < positions.length; i++) {
|
|
410
|
+
const position = positions[i]
|
|
411
|
+
const curDist = end > position ? end - position : position - end
|
|
412
|
+
if (curDist < dist) {
|
|
413
|
+
dist = curDist
|
|
414
|
+
closestPoint = i
|
|
425
415
|
}
|
|
426
|
-
// we could do some detection of other touchables and cancel here..
|
|
427
|
-
return Math.abs(dy) > 5
|
|
428
416
|
}
|
|
417
|
+
// have to call both because state may not change but need to snap back
|
|
418
|
+
setPosition(closestPoint)
|
|
419
|
+
animateTo(closestPoint)
|
|
420
|
+
}
|
|
429
421
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
}
|
|
422
|
+
const finish = (_e: GestureResponderEvent, state: PanResponderGestureState) => {
|
|
423
|
+
release({
|
|
424
|
+
vy: state.vy,
|
|
425
|
+
dragAt: state.dy,
|
|
426
|
+
})
|
|
427
|
+
}
|
|
435
428
|
|
|
436
|
-
|
|
429
|
+
let previouslyScrolling = false
|
|
437
430
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
431
|
+
const onMoveShouldSet = (_e: GestureResponderEvent, { dy }: PanResponderGestureState) => {
|
|
432
|
+
const isScrolled = scrollBridge.y !== 0
|
|
433
|
+
const isDraggingUp = dy < 0
|
|
434
|
+
const isAtTop = scrollBridge.paneY <= scrollBridge.paneMinY
|
|
435
|
+
if (isScrolled) {
|
|
436
|
+
previouslyScrolling = true
|
|
437
|
+
return false
|
|
438
|
+
}
|
|
439
|
+
if (previouslyScrolling) {
|
|
440
|
+
previouslyScrolling = false
|
|
441
|
+
return true
|
|
442
|
+
}
|
|
443
|
+
// prevent drag once at top and pulling up
|
|
444
|
+
if (isAtTop) {
|
|
445
|
+
if (!isScrolled && isDraggingUp) {
|
|
446
|
+
return false
|
|
442
447
|
}
|
|
443
|
-
const to = dy + startY
|
|
444
|
-
animatedNumber.setValue(resisted(to, minY), { type: 'direct' })
|
|
445
448
|
}
|
|
449
|
+
// we could do some detection of other touchables and cancel here..
|
|
450
|
+
return Math.abs(dy) > 5
|
|
451
|
+
}
|
|
446
452
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
onPanResponderMove: (_e, { dy }) => {
|
|
453
|
-
const toFull = dy + startY
|
|
454
|
-
const to = resisted(toFull, minY)
|
|
455
|
-
animatedNumber.setValue(to, { type: 'direct' })
|
|
456
|
-
},
|
|
457
|
-
onPanResponderEnd: finish,
|
|
458
|
-
onPanResponderTerminate: finish,
|
|
459
|
-
onPanResponderRelease: finish,
|
|
460
|
-
})
|
|
461
|
-
},
|
|
462
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
463
|
-
[disableDrag, animateTo, frameSize, positions, setPosition]
|
|
464
|
-
)
|
|
453
|
+
const grant = () => {
|
|
454
|
+
makeUnselectable(true)
|
|
455
|
+
stopSpring()
|
|
456
|
+
startY = at.current
|
|
457
|
+
}
|
|
465
458
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
if (isValidElement(child)) {
|
|
473
|
-
const name = child.type?.['staticConfig']?.componentName
|
|
474
|
-
switch (name) {
|
|
475
|
-
case 'SheetHandle':
|
|
476
|
-
handleComponent = child
|
|
477
|
-
break
|
|
478
|
-
case 'Sheet':
|
|
479
|
-
frameComponent = child
|
|
480
|
-
break
|
|
481
|
-
case 'SheetOverlay':
|
|
482
|
-
overlayComponent = child
|
|
483
|
-
break
|
|
484
|
-
default:
|
|
485
|
-
// eslint-disable-next-line no-console
|
|
486
|
-
console.warn('Warning: passed invalid child to Sheet', child)
|
|
459
|
+
let isExternalDrag = false
|
|
460
|
+
|
|
461
|
+
scrollBridge.drag = (dy) => {
|
|
462
|
+
if (!isExternalDrag) {
|
|
463
|
+
isExternalDrag = true
|
|
464
|
+
grant()
|
|
487
465
|
}
|
|
466
|
+
const to = dy + startY
|
|
467
|
+
animatedNumber.setValue(resisted(to, minY), { type: 'direct' })
|
|
488
468
|
}
|
|
489
|
-
})
|
|
490
469
|
|
|
491
|
-
|
|
470
|
+
scrollBridge.release = release
|
|
471
|
+
|
|
472
|
+
return PanResponder.create({
|
|
473
|
+
onMoveShouldSetPanResponder: onMoveShouldSet,
|
|
474
|
+
onPanResponderGrant: grant,
|
|
475
|
+
onPanResponderMove: (_e, { dy }) => {
|
|
476
|
+
const toFull = dy + startY
|
|
477
|
+
const to = resisted(toFull, minY)
|
|
478
|
+
animatedNumber.setValue(to, { type: 'direct' })
|
|
479
|
+
},
|
|
480
|
+
onPanResponderEnd: finish,
|
|
481
|
+
onPanResponderTerminate: finish,
|
|
482
|
+
onPanResponderRelease: finish,
|
|
483
|
+
})
|
|
484
|
+
},
|
|
485
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
486
|
+
[disableDrag, animateTo, frameSize, positions, setPosition]
|
|
487
|
+
)
|
|
492
488
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
489
|
+
let handleComponent: React.ReactElement | null = null
|
|
490
|
+
let overlayComponent: React.ReactElement | null = null
|
|
491
|
+
let frameComponent: React.ReactElement | null = null
|
|
492
|
+
|
|
493
|
+
// TODO do more radix-like and don't require direct children descendents
|
|
494
|
+
React.Children.forEach(childrenProp, (child) => {
|
|
495
|
+
if (isValidElement(child)) {
|
|
496
|
+
const name = child.type?.['staticConfig']?.componentName
|
|
497
|
+
switch (name) {
|
|
498
|
+
case 'SheetHandle':
|
|
499
|
+
handleComponent = child
|
|
500
|
+
break
|
|
501
|
+
case 'Sheet':
|
|
502
|
+
frameComponent = child
|
|
503
|
+
break
|
|
504
|
+
case 'SheetOverlay':
|
|
505
|
+
overlayComponent = child
|
|
506
|
+
break
|
|
507
|
+
default:
|
|
508
|
+
// eslint-disable-next-line no-console
|
|
509
|
+
console.warn('Warning: passed invalid child to Sheet', child)
|
|
496
510
|
}
|
|
497
|
-
}
|
|
511
|
+
}
|
|
512
|
+
})
|
|
498
513
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
*/
|
|
505
|
-
const [isShowingInnerSheet, setIsShowingInnerSheet] = useState(false)
|
|
506
|
-
const shouldHideParentSheet = !isWeb && modal && isShowingInnerSheet
|
|
507
|
-
const parentSheetContext = useContext(SheetInsideSheetContext)
|
|
508
|
-
const onInnerSheet = useCallback((hasChild: boolean) => {
|
|
509
|
-
setIsShowingInnerSheet(hasChild)
|
|
510
|
-
}, [])
|
|
511
|
-
|
|
512
|
-
useIsomorphicLayoutEffect(() => {
|
|
513
|
-
if (!parentSheetContext || !open) return
|
|
514
|
-
parentSheetContext(true)
|
|
515
|
-
return () => {
|
|
516
|
-
parentSheetContext(false)
|
|
517
|
-
}
|
|
518
|
-
}, [parentSheetContext, open])
|
|
514
|
+
const animatedStyle = driver.useAnimatedNumberStyle(animatedNumber, (val) => {
|
|
515
|
+
return {
|
|
516
|
+
transform: [{ translateY: frameSize === 0 ? HIDDEN_SIZE : val }],
|
|
517
|
+
}
|
|
518
|
+
})
|
|
519
519
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
520
|
+
// temp until reanimated useAnimatedNumber fix
|
|
521
|
+
const AnimatedView = driver['NumberView'] ?? driver.View
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* This is a hacky workaround for native:
|
|
525
|
+
*/
|
|
526
|
+
const [isShowingInnerSheet, setIsShowingInnerSheet] = useState(false)
|
|
527
|
+
const shouldHideParentSheet = !isWeb && modal && isShowingInnerSheet
|
|
528
|
+
const parentSheetContext = useContext(SheetInsideSheetContext)
|
|
529
|
+
const onInnerSheet = useCallback((hasChild: boolean) => {
|
|
530
|
+
setIsShowingInnerSheet(hasChild)
|
|
531
|
+
}, [])
|
|
532
|
+
|
|
533
|
+
useIsomorphicLayoutEffect(() => {
|
|
534
|
+
if (!parentSheetContext || !open) return
|
|
535
|
+
parentSheetContext(true)
|
|
536
|
+
return () => {
|
|
537
|
+
parentSheetContext(false)
|
|
538
|
+
}
|
|
539
|
+
}, [parentSheetContext, open])
|
|
526
540
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
541
|
+
const nextParentContext = useMemo(
|
|
542
|
+
() => ({
|
|
543
|
+
zIndex,
|
|
544
|
+
}),
|
|
545
|
+
[zIndex]
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
const contents = (
|
|
549
|
+
<ParentSheetContext.Provider value={nextParentContext}>
|
|
550
|
+
<SheetProvider
|
|
551
|
+
modal={modal}
|
|
552
|
+
contentRef={contentRef}
|
|
553
|
+
dismissOnOverlayPress={dismissOnOverlayPress}
|
|
554
|
+
dismissOnSnapToBottom={dismissOnSnapToBottom}
|
|
555
|
+
open={open}
|
|
556
|
+
hidden={isHidden}
|
|
557
|
+
scope={__scopeSheet}
|
|
558
|
+
position={position}
|
|
559
|
+
snapPoints={snapPoints}
|
|
560
|
+
setPosition={setPosition}
|
|
561
|
+
setOpen={setOpen}
|
|
562
|
+
scrollBridge={scrollBridge}
|
|
563
|
+
>
|
|
564
|
+
{isResizing || shouldHideParentSheet ? null : overlayComponent}
|
|
565
|
+
|
|
566
|
+
<AnimatedView
|
|
567
|
+
ref={ref}
|
|
568
|
+
{...panResponder?.panHandlers}
|
|
569
|
+
onLayout={(e) => {
|
|
570
|
+
const next = e.nativeEvent.layout.height
|
|
571
|
+
setFrameSize((prev) => {
|
|
572
|
+
const isBigChange = Math.abs(prev - next) > 50
|
|
573
|
+
setIsResizing(isBigChange)
|
|
574
|
+
return next
|
|
575
|
+
})
|
|
576
|
+
}}
|
|
577
|
+
pointerEvents={open && !shouldHideParentSheet ? 'auto' : 'none'}
|
|
578
|
+
style={[
|
|
579
|
+
{
|
|
580
|
+
position: 'absolute',
|
|
581
|
+
zIndex,
|
|
582
|
+
width: '100%',
|
|
583
|
+
height: '100%',
|
|
584
|
+
opacity: shouldHideParentSheet ? 0 : 1,
|
|
585
|
+
},
|
|
586
|
+
animatedStyle,
|
|
587
|
+
]}
|
|
542
588
|
>
|
|
543
|
-
{
|
|
544
|
-
|
|
545
|
-
<
|
|
546
|
-
|
|
547
|
-
{
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
setIsResizing(isBigChange)
|
|
553
|
-
return next
|
|
554
|
-
})
|
|
555
|
-
}}
|
|
556
|
-
pointerEvents={open && !shouldHideParentSheet ? 'auto' : 'none'}
|
|
557
|
-
style={[
|
|
558
|
-
{
|
|
559
|
-
position: 'absolute',
|
|
560
|
-
zIndex,
|
|
561
|
-
width: '100%',
|
|
562
|
-
height: '100%',
|
|
563
|
-
opacity: shouldHideParentSheet ? 0 : 1,
|
|
564
|
-
},
|
|
565
|
-
animatedStyle,
|
|
566
|
-
]}
|
|
589
|
+
{handleComponent}
|
|
590
|
+
|
|
591
|
+
<RemoveScroll
|
|
592
|
+
enabled={open && modal && handleDisableScroll}
|
|
593
|
+
as={Slot}
|
|
594
|
+
allowPinchZoom
|
|
595
|
+
shards={[contentRef]}
|
|
596
|
+
// causes lots of bugs on touch web on site
|
|
597
|
+
removeScrollBar={false}
|
|
567
598
|
>
|
|
568
|
-
{
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
</AnimatedView>
|
|
581
|
-
</SheetProvider>
|
|
582
|
-
</ParentSheetContext.Provider>
|
|
599
|
+
{isResizing ? null : frameComponent}
|
|
600
|
+
</RemoveScroll>
|
|
601
|
+
</AnimatedView>
|
|
602
|
+
</SheetProvider>
|
|
603
|
+
</ParentSheetContext.Provider>
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
if (modal) {
|
|
607
|
+
const modalContents = (
|
|
608
|
+
<Portal zIndex={zIndex}>
|
|
609
|
+
<Theme name={themeName}>{contents}</Theme>
|
|
610
|
+
</Portal>
|
|
583
611
|
)
|
|
584
612
|
|
|
585
|
-
if (
|
|
586
|
-
return
|
|
613
|
+
if (isWeb) {
|
|
614
|
+
return modalContents
|
|
587
615
|
}
|
|
588
616
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
if (isWeb) {
|
|
597
|
-
return modalContents
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
// on native we don't support multiple modals yet... fix for now is to hide outer one
|
|
601
|
-
return (
|
|
602
|
-
<SheetInsideSheetContext.Provider value={onInnerSheet}>
|
|
603
|
-
{modalContents}
|
|
604
|
-
</SheetInsideSheetContext.Provider>
|
|
605
|
-
)
|
|
606
|
-
}
|
|
617
|
+
// on native we don't support multiple modals yet... fix for now is to hide outer one
|
|
618
|
+
return (
|
|
619
|
+
<SheetInsideSheetContext.Provider value={onInnerSheet}>
|
|
620
|
+
{modalContents}
|
|
621
|
+
</SheetInsideSheetContext.Provider>
|
|
622
|
+
)
|
|
623
|
+
}
|
|
607
624
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
),
|
|
611
|
-
sheetComponents
|
|
625
|
+
return contents
|
|
626
|
+
})
|
|
612
627
|
)
|
|
613
628
|
|
|
614
629
|
const SheetInsideSheetContext = createContext<((hasChild: boolean) => void) | null>(null)
|