@tamagui/sheet 1.0.1-beta.100 → 1.0.1-beta.103
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 +244 -112
- package/dist/cjs/Sheet.js.map +2 -2
- package/dist/esm/Sheet.js +253 -109
- package/dist/esm/Sheet.js.map +2 -2
- package/dist/jsx/Sheet.js +233 -100
- package/dist/jsx/Sheet.js.map +2 -2
- package/package.json +6 -6
- package/src/Sheet.tsx +328 -134
- package/types/Sheet.d.ts +73 -87
- package/types/Sheet.d.ts.map +1 -1
- package/types/Drawer.d.ts +0 -184
- package/types/Drawer.d.ts.map +0 -1
package/src/Sheet.tsx
CHANGED
|
@@ -1,29 +1,83 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
GetProps,
|
|
3
|
+
Theme,
|
|
4
|
+
isClient,
|
|
5
|
+
isWeb,
|
|
6
|
+
mergeEvent,
|
|
7
|
+
styled,
|
|
8
|
+
themeable,
|
|
9
|
+
useEvent,
|
|
10
|
+
useIsomorphicLayoutEffect,
|
|
11
|
+
useThemeName,
|
|
12
|
+
withStaticProperties,
|
|
13
|
+
} from '@tamagui/core'
|
|
2
14
|
import { ScopedProps, createContextScope } from '@tamagui/create-context'
|
|
15
|
+
import { Portal } from '@tamagui/portal'
|
|
3
16
|
import { XStack, XStackProps, YStack } from '@tamagui/stacks'
|
|
4
17
|
import { useControllableState } from '@tamagui/use-controllable-state'
|
|
5
18
|
import React, {
|
|
6
19
|
ReactNode,
|
|
20
|
+
createContext,
|
|
7
21
|
forwardRef,
|
|
8
22
|
isValidElement,
|
|
9
|
-
|
|
23
|
+
useContext,
|
|
10
24
|
useEffect,
|
|
11
25
|
useLayoutEffect,
|
|
12
26
|
useMemo,
|
|
13
27
|
useRef,
|
|
14
28
|
useState,
|
|
15
29
|
} from 'react'
|
|
16
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
Animated,
|
|
32
|
+
GestureResponderEvent,
|
|
33
|
+
PanResponder,
|
|
34
|
+
PanResponderGestureState,
|
|
35
|
+
View,
|
|
36
|
+
} from 'react-native'
|
|
37
|
+
|
|
38
|
+
const SHEET_NAME = 'Sheet'
|
|
39
|
+
const SHEET_HANDLE_NAME = 'SheetHandle'
|
|
40
|
+
|
|
41
|
+
export type SheetProps = ScopedProps<
|
|
42
|
+
{
|
|
43
|
+
open?: boolean
|
|
44
|
+
defaultOpen?: boolean
|
|
45
|
+
onChangeOpen?: OpenChangeHandler
|
|
46
|
+
position?: number
|
|
47
|
+
defaultPosition?: number
|
|
48
|
+
snapPoints?: number[]
|
|
49
|
+
onChangePosition?: PositionChangeHandler
|
|
50
|
+
children?: ReactNode
|
|
51
|
+
dismissOnOverlayPress?: boolean
|
|
52
|
+
animationConfig?: Animated.SpringAnimationConfig
|
|
53
|
+
disableDrag?: boolean
|
|
54
|
+
modal?: boolean
|
|
55
|
+
},
|
|
56
|
+
'Sheet'
|
|
57
|
+
>
|
|
17
58
|
|
|
18
59
|
type PositionChangeHandler =
|
|
19
60
|
| ((position: number) => void)
|
|
20
61
|
| React.Dispatch<React.SetStateAction<number>>
|
|
21
62
|
|
|
22
|
-
|
|
23
|
-
|
|
63
|
+
type OpenChangeHandler = ((open: boolean) => void) | React.Dispatch<React.SetStateAction<boolean>>
|
|
64
|
+
|
|
65
|
+
type SheetContextValue = Required<
|
|
66
|
+
Pick<SheetProps, 'open' | 'position' | 'snapPoints' | 'dismissOnOverlayPress'>
|
|
67
|
+
> & {
|
|
68
|
+
hidden: boolean
|
|
69
|
+
setPosition: React.Dispatch<React.SetStateAction<number>>
|
|
70
|
+
setOpen: React.Dispatch<React.SetStateAction<boolean>>
|
|
71
|
+
}
|
|
24
72
|
|
|
25
|
-
|
|
26
|
-
|
|
73
|
+
const [createSheetContext, createSheetScope] = createContextScope(SHEET_NAME)
|
|
74
|
+
const [SheetProvider, useSheetContext] = createSheetContext<SheetContextValue>(
|
|
75
|
+
SHEET_NAME,
|
|
76
|
+
{} as any
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
export const SheetHandleFrame = styled(XStack, {
|
|
80
|
+
name: SHEET_HANDLE_NAME,
|
|
27
81
|
height: 10,
|
|
28
82
|
borderRadius: 100,
|
|
29
83
|
backgroundColor: '$background',
|
|
@@ -41,12 +95,18 @@ export const DrawerHandleFrame = styled(XStack, {
|
|
|
41
95
|
},
|
|
42
96
|
})
|
|
43
97
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
98
|
+
type SheetScopedProps<A> = ScopedProps<A, 'Sheet'>
|
|
99
|
+
|
|
100
|
+
export const SheetHandle = SheetHandleFrame.extractable(
|
|
101
|
+
({ __scopeSheet, ...props }: SheetScopedProps<XStackProps>) => {
|
|
102
|
+
const context = useSheetContext(SHEET_HANDLE_NAME, __scopeSheet)
|
|
103
|
+
|
|
104
|
+
if (context.open === false) {
|
|
105
|
+
return null
|
|
106
|
+
}
|
|
47
107
|
|
|
48
108
|
return (
|
|
49
|
-
<
|
|
109
|
+
<SheetHandleFrame
|
|
50
110
|
onPress={() => {
|
|
51
111
|
const nextPos = (context.position + 1) % context.snapPoints.length
|
|
52
112
|
context.setPosition(nextPos)
|
|
@@ -57,145 +117,231 @@ export const DrawerHandle = DrawerHandleFrame.extractable(
|
|
|
57
117
|
}
|
|
58
118
|
)
|
|
59
119
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
setPosition: React.Dispatch<React.SetStateAction<number>>
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const [createDrawerContext, createDrawerScope] = createContextScope(DRAWER_NAME)
|
|
67
|
-
const [DrawerProvider, useDrawerContext] = createDrawerContext<DrawerContextValue>(
|
|
68
|
-
DRAWER_NAME,
|
|
69
|
-
{} as any
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
export const DrawerBackdrop = styled(YStack, {
|
|
73
|
-
name: 'DrawerBackdrop',
|
|
120
|
+
export const SheetOverlayFrame = styled(YStack, {
|
|
121
|
+
name: 'SheetOverlay',
|
|
122
|
+
// TODO this should be $background without opacity and just customized by theme
|
|
74
123
|
backgroundColor: '$color',
|
|
75
124
|
fullscreen: true,
|
|
76
125
|
opacity: 0.2,
|
|
126
|
+
zIndex: 0,
|
|
127
|
+
|
|
128
|
+
variants: {
|
|
129
|
+
closed: {
|
|
130
|
+
true: {
|
|
131
|
+
opacity: 0,
|
|
132
|
+
pointerEvents: 'none',
|
|
133
|
+
},
|
|
134
|
+
false: {
|
|
135
|
+
pointerEvents: 'auto',
|
|
136
|
+
},
|
|
137
|
+
// TODO still have as const bug
|
|
138
|
+
} as const,
|
|
139
|
+
},
|
|
77
140
|
})
|
|
78
141
|
|
|
79
|
-
export
|
|
80
|
-
|
|
142
|
+
export type SheetOverlayProps = GetProps<typeof SheetOverlayFrame>
|
|
143
|
+
|
|
144
|
+
export const SheetOverlay = SheetOverlayFrame.extractable(
|
|
145
|
+
({ __scopeSheet, ...props }: SheetScopedProps<SheetOverlayProps>) => {
|
|
146
|
+
const context = useSheetContext(SHEET_HANDLE_NAME, __scopeSheet)
|
|
147
|
+
return (
|
|
148
|
+
<SheetOverlayFrame
|
|
149
|
+
closed={!context.open || context.hidden}
|
|
150
|
+
{...props}
|
|
151
|
+
onPress={mergeEvent(
|
|
152
|
+
props.onPress,
|
|
153
|
+
context.dismissOnOverlayPress
|
|
154
|
+
? () => {
|
|
155
|
+
context.setOpen(false)
|
|
156
|
+
}
|
|
157
|
+
: undefined
|
|
158
|
+
)}
|
|
159
|
+
/>
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
const selectionStyleSheet = isClient ? document.createElement('style') : null
|
|
165
|
+
if (selectionStyleSheet) {
|
|
166
|
+
document.head.appendChild(selectionStyleSheet)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export const SheetFrame = styled(YStack, {
|
|
170
|
+
name: 'SheetFrame',
|
|
81
171
|
flex: 1,
|
|
82
172
|
backgroundColor: '$background',
|
|
83
173
|
borderTopLeftRadius: '$4',
|
|
84
174
|
borderTopRightRadius: '$4',
|
|
85
175
|
padding: '$4',
|
|
86
176
|
width: '100%',
|
|
177
|
+
pointerEvents: 'auto',
|
|
87
178
|
})
|
|
88
179
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
open?: boolean
|
|
92
|
-
defaultOpen?: boolean
|
|
93
|
-
defaultPosition?: number
|
|
94
|
-
snapPoints?: number[]
|
|
95
|
-
position?: number
|
|
96
|
-
onChangePosition?: PositionChangeHandler
|
|
97
|
-
children?: ReactNode
|
|
98
|
-
},
|
|
99
|
-
'Drawer'
|
|
100
|
-
>
|
|
101
|
-
|
|
102
|
-
const useIsSSR = () => {
|
|
103
|
-
const [val, setVal] = useState(isWeb ? isSSR : false)
|
|
104
|
-
useEffect(() => {
|
|
105
|
-
if (isWeb && !isSSR) {
|
|
106
|
-
setVal(false)
|
|
107
|
-
}
|
|
108
|
-
}, [])
|
|
109
|
-
return val
|
|
110
|
-
}
|
|
180
|
+
// set all the way off screen
|
|
181
|
+
const HIDDEN_SIZE = 10_000
|
|
111
182
|
|
|
112
|
-
export const
|
|
183
|
+
export const Sheet = withStaticProperties(
|
|
113
184
|
themeable(
|
|
114
|
-
forwardRef<View,
|
|
185
|
+
forwardRef<View, SheetProps>((props, ref) => {
|
|
115
186
|
const {
|
|
116
|
-
|
|
187
|
+
__scopeSheet,
|
|
117
188
|
snapPoints: snapPointsProp = [80, 10],
|
|
189
|
+
open: openProp,
|
|
190
|
+
defaultOpen,
|
|
118
191
|
children: childrenProp,
|
|
119
192
|
position: positionProp,
|
|
120
193
|
onChangePosition,
|
|
194
|
+
onChangeOpen,
|
|
121
195
|
defaultPosition,
|
|
122
|
-
|
|
196
|
+
dismissOnOverlayPress = true,
|
|
197
|
+
animationConfig,
|
|
198
|
+
disableDrag: disableDragProp,
|
|
199
|
+
modal,
|
|
123
200
|
} = props
|
|
124
|
-
const isServerSide = useIsSSR()
|
|
125
201
|
|
|
126
|
-
|
|
202
|
+
// allows for sheets to be controlled by other components
|
|
203
|
+
const controller = useContext(SheetControllerContext)
|
|
204
|
+
const isHidden = controller?.hidden || false
|
|
205
|
+
const disableDrag = disableDragProp ?? controller?.disableDrag
|
|
206
|
+
const themeName = useThemeName()
|
|
207
|
+
|
|
208
|
+
const onChangeOpenInternal = (val: boolean) => {
|
|
209
|
+
controller?.onChangeOpen?.(val)
|
|
210
|
+
onChangeOpen?.(val)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const [open, setOpen] = useControllableState({
|
|
214
|
+
prop: controller?.open ?? openProp,
|
|
215
|
+
defaultProp: defaultOpen || true,
|
|
216
|
+
onChange: onChangeOpenInternal,
|
|
217
|
+
strategy: controller ? 'most-recent-wins' : 'prop-wins',
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
const [frameSize, setFrameSize] = useState<number>(0)
|
|
221
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
222
|
+
const snapPoints = useMemo(() => snapPointsProp, [JSON.stringify(snapPointsProp)])
|
|
223
|
+
|
|
224
|
+
// lets set -1 to be always the "open = false" position
|
|
225
|
+
const [position_, setPosition] = useControllableState({
|
|
127
226
|
prop: positionProp,
|
|
128
|
-
defaultProp: defaultPosition || 0,
|
|
227
|
+
defaultProp: defaultPosition || (open ? 0 : -1),
|
|
129
228
|
onChange: onChangePosition,
|
|
130
|
-
strategy: 'most-recent-wins',
|
|
131
229
|
})
|
|
132
|
-
|
|
133
|
-
const [layout, setLayout] = useState<LayoutRectangle>()
|
|
230
|
+
const position = open === false ? -1 : position_
|
|
134
231
|
|
|
135
232
|
const positionValue = useRef<Animated.Value>()
|
|
136
233
|
if (!positionValue.current) {
|
|
137
|
-
positionValue.current = new Animated.Value(
|
|
234
|
+
positionValue.current = new Animated.Value(HIDDEN_SIZE)
|
|
138
235
|
}
|
|
139
236
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
237
|
+
const [isResizing, setIsResizing] = useState(true)
|
|
238
|
+
useLayoutEffect(() => {
|
|
239
|
+
if (!isResizing) {
|
|
240
|
+
setIsResizing(true)
|
|
241
|
+
}
|
|
242
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
243
|
+
}, [modal])
|
|
147
244
|
|
|
148
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
149
245
|
const spring = useRef<Animated.CompositeAnimation | null>(null)
|
|
150
|
-
|
|
151
246
|
function stopSpring() {
|
|
152
247
|
spring.current?.stop()
|
|
153
248
|
spring.current = null
|
|
154
249
|
}
|
|
155
250
|
|
|
156
|
-
//
|
|
251
|
+
// open must set position
|
|
252
|
+
const shouldSetPositionOpen = open && position < 0
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
if (shouldSetPositionOpen) {
|
|
255
|
+
setPosition(0)
|
|
256
|
+
}
|
|
257
|
+
}, [setPosition, shouldSetPositionOpen])
|
|
258
|
+
|
|
157
259
|
const positions = useMemo(
|
|
158
|
-
() => snapPoints.map((point) =>
|
|
159
|
-
[
|
|
260
|
+
() => snapPoints.map((point) => getPercentSize(point, frameSize)),
|
|
261
|
+
[frameSize, snapPoints]
|
|
160
262
|
)
|
|
161
263
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
264
|
+
const animateTo = useEvent((position: number) => {
|
|
265
|
+
const pos = positionValue.current
|
|
266
|
+
if (isHidden && open) return
|
|
267
|
+
if (!pos) return
|
|
268
|
+
if (frameSize === 0) return
|
|
269
|
+
const hiddenValue = frameSize === 0 ? HIDDEN_SIZE : frameSize
|
|
270
|
+
const toValue = isHidden || position === -1 ? hiddenValue : positions[position]
|
|
271
|
+
if (pos['_value'] === toValue) return
|
|
272
|
+
stopSpring()
|
|
273
|
+
if (isHidden || isResizing) {
|
|
274
|
+
if (isResizing) {
|
|
275
|
+
setIsResizing(false)
|
|
276
|
+
}
|
|
277
|
+
Animated.timing(pos, {
|
|
169
278
|
useNativeDriver: !isWeb,
|
|
170
279
|
toValue,
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
280
|
+
duration: 0,
|
|
281
|
+
}).start()
|
|
282
|
+
return
|
|
283
|
+
}
|
|
284
|
+
// dont bounce on initial measure to bottom
|
|
285
|
+
const overshootClamping = pos['_value'] === HIDDEN_SIZE
|
|
286
|
+
spring.current = Animated.spring(pos, {
|
|
287
|
+
useNativeDriver: !isWeb,
|
|
288
|
+
toValue,
|
|
289
|
+
overshootClamping,
|
|
290
|
+
...animationConfig,
|
|
291
|
+
})
|
|
292
|
+
spring.current.start(({ finished }) => finished && stopSpring())
|
|
293
|
+
})
|
|
176
294
|
|
|
177
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
178
295
|
useLayoutEffect(() => {
|
|
179
296
|
animateTo(position)
|
|
180
|
-
}, [position, animateTo])
|
|
297
|
+
}, [isHidden, frameSize, position, animateTo])
|
|
181
298
|
|
|
182
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
183
299
|
const panResponder = useMemo(() => {
|
|
184
|
-
if (
|
|
185
|
-
|
|
300
|
+
if (disableDrag) return
|
|
301
|
+
if (!frameSize) return
|
|
186
302
|
|
|
187
303
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
188
304
|
const pos = positionValue.current!
|
|
189
305
|
const minY = positions[0]
|
|
190
306
|
let startY = pos['_value']
|
|
191
307
|
|
|
308
|
+
function makeUnselectable(val: boolean) {
|
|
309
|
+
if (!selectionStyleSheet) return
|
|
310
|
+
if (!val) {
|
|
311
|
+
selectionStyleSheet.innerText = ``
|
|
312
|
+
} else {
|
|
313
|
+
selectionStyleSheet.innerText = `:root * { user-select: none !important; -webkit-user-select: none !important; }`
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const finish = (_e: GestureResponderEvent, { vy, dy }: PanResponderGestureState) => {
|
|
318
|
+
makeUnselectable(false)
|
|
319
|
+
const at = dy + startY
|
|
320
|
+
// seems liky vy goes up to about 4 at the very most (+ is down, - is up)
|
|
321
|
+
// lets base our multiplier on the total layout height
|
|
322
|
+
const end = at + frameSize * vy * 0.33
|
|
323
|
+
let closestPoint = 0
|
|
324
|
+
let dist = Infinity
|
|
325
|
+
for (let i = 0; i < positions.length; i++) {
|
|
326
|
+
const position = positions[i]
|
|
327
|
+
const curDist = end > position ? end - position : position - end
|
|
328
|
+
if (curDist < dist) {
|
|
329
|
+
dist = curDist
|
|
330
|
+
closestPoint = i
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// have to call both because state may not change but need to snap back
|
|
334
|
+
setPosition(closestPoint)
|
|
335
|
+
animateTo(closestPoint)
|
|
336
|
+
}
|
|
337
|
+
|
|
192
338
|
return PanResponder.create({
|
|
193
339
|
onMoveShouldSetPanResponder: (_e, { dy }) => {
|
|
194
340
|
// we could do some detection of other touchables and cancel here..
|
|
195
|
-
// console.log('wut is', _e)
|
|
196
341
|
return Math.abs(dy) > 6
|
|
197
342
|
},
|
|
198
343
|
onPanResponderGrant: () => {
|
|
344
|
+
makeUnselectable(true)
|
|
199
345
|
stopSpring()
|
|
200
346
|
startY = pos['_value']
|
|
201
347
|
},
|
|
@@ -203,94 +349,110 @@ export const Drawer = withStaticProperties(
|
|
|
203
349
|
const to = dy + startY
|
|
204
350
|
pos.setValue(resisted(to, minY))
|
|
205
351
|
},
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
// lets base our multiplier on the total layout height
|
|
210
|
-
const end = at + layout.height * vy * 0.33
|
|
211
|
-
let closestPoint = 0
|
|
212
|
-
let dist = Infinity
|
|
213
|
-
for (let i = 0; i < positions.length; i++) {
|
|
214
|
-
const position = positions[i]
|
|
215
|
-
const curDist = end > position ? end - position : position - end
|
|
216
|
-
if (curDist < dist) {
|
|
217
|
-
dist = curDist
|
|
218
|
-
closestPoint = i
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
// have to call both because state may not change but need to snap back
|
|
222
|
-
setPosition(closestPoint)
|
|
223
|
-
animateTo(closestPoint)
|
|
224
|
-
},
|
|
352
|
+
onPanResponderEnd: finish,
|
|
353
|
+
onPanResponderTerminate: finish,
|
|
354
|
+
onPanResponderRelease: finish,
|
|
225
355
|
})
|
|
226
|
-
}, [
|
|
356
|
+
}, [disableDrag, animateTo, frameSize, positions, setPosition])
|
|
227
357
|
|
|
228
358
|
let handleComponent: React.ReactElement | null = null
|
|
229
|
-
let
|
|
359
|
+
let overlayComponent: React.ReactElement | null = null
|
|
230
360
|
let frameComponent: React.ReactElement | null = null
|
|
231
361
|
|
|
362
|
+
// TODO do more radix-like and don't require direct children descendents
|
|
232
363
|
React.Children.forEach(childrenProp, (child) => {
|
|
233
364
|
if (isValidElement(child)) {
|
|
234
|
-
|
|
235
|
-
|
|
365
|
+
const name = child.type?.['staticConfig']?.componentName
|
|
366
|
+
switch (name) {
|
|
367
|
+
case 'SheetHandle':
|
|
236
368
|
handleComponent = child
|
|
237
369
|
break
|
|
238
|
-
case '
|
|
370
|
+
case 'SheetFrame':
|
|
239
371
|
frameComponent = child
|
|
240
372
|
break
|
|
241
|
-
case '
|
|
242
|
-
|
|
373
|
+
case 'SheetOverlay':
|
|
374
|
+
overlayComponent = child
|
|
243
375
|
break
|
|
244
376
|
default:
|
|
245
|
-
console.warn('Warning: passed invalid child to
|
|
377
|
+
console.warn('Warning: passed invalid child to Sheet', child)
|
|
246
378
|
}
|
|
247
379
|
}
|
|
248
380
|
})
|
|
249
381
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
382
|
+
const preventShown = controller?.hidden && controller?.open
|
|
383
|
+
|
|
384
|
+
if (preventShown) {
|
|
385
|
+
return null
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const contents = (
|
|
389
|
+
<SheetProvider
|
|
390
|
+
dismissOnOverlayPress={dismissOnOverlayPress}
|
|
391
|
+
open={open}
|
|
392
|
+
hidden={isHidden}
|
|
393
|
+
scope={__scopeSheet}
|
|
253
394
|
position={position}
|
|
254
395
|
snapPoints={snapPoints}
|
|
255
396
|
setPosition={setPosition}
|
|
397
|
+
setOpen={setOpen}
|
|
256
398
|
>
|
|
257
|
-
{
|
|
258
|
-
{
|
|
399
|
+
{isResizing ? null : overlayComponent}
|
|
400
|
+
{/* no fancy hidden animation etc for handle for now */}
|
|
401
|
+
{isHidden ? null : handleComponent}
|
|
259
402
|
<Animated.View
|
|
403
|
+
ref={ref}
|
|
260
404
|
{...panResponder?.panHandlers}
|
|
261
405
|
onLayout={(e) => {
|
|
262
|
-
|
|
406
|
+
const next = e.nativeEvent.layout.height
|
|
407
|
+
setFrameSize((prev) => {
|
|
408
|
+
const isBigChange = Math.abs(prev - next) > 50
|
|
409
|
+
setIsResizing(isBigChange)
|
|
410
|
+
return next
|
|
411
|
+
})
|
|
263
412
|
}}
|
|
413
|
+
pointerEvents={open ? 'auto' : 'none'}
|
|
264
414
|
style={{
|
|
415
|
+
position: 'absolute',
|
|
416
|
+
zIndex: 10,
|
|
265
417
|
width: '100%',
|
|
266
418
|
height: '100%',
|
|
267
|
-
transform: [{ translateY: positionValue.current }],
|
|
419
|
+
transform: [{ translateY: frameSize === 0 ? HIDDEN_SIZE : positionValue.current }],
|
|
268
420
|
}}
|
|
269
421
|
>
|
|
270
|
-
{frameComponent}
|
|
422
|
+
{isResizing ? null : frameComponent}
|
|
271
423
|
</Animated.View>
|
|
272
|
-
</
|
|
424
|
+
</SheetProvider>
|
|
273
425
|
)
|
|
426
|
+
|
|
427
|
+
if (modal) {
|
|
428
|
+
return (
|
|
429
|
+
<Portal>
|
|
430
|
+
<Theme name={themeName}>{contents}</Theme>
|
|
431
|
+
</Portal>
|
|
432
|
+
)
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return contents
|
|
274
436
|
}),
|
|
275
437
|
{
|
|
276
|
-
componentName: '
|
|
438
|
+
componentName: 'Sheet',
|
|
277
439
|
}
|
|
278
440
|
),
|
|
279
441
|
{
|
|
280
|
-
Handle:
|
|
281
|
-
Frame:
|
|
282
|
-
|
|
442
|
+
Handle: SheetHandle,
|
|
443
|
+
Frame: SheetFrame,
|
|
444
|
+
Overlay: SheetOverlay,
|
|
283
445
|
}
|
|
284
446
|
)
|
|
285
447
|
|
|
286
|
-
function
|
|
287
|
-
if (!
|
|
448
|
+
function getPercentSize(point?: number, frameSize?: number) {
|
|
449
|
+
if (!frameSize) return 0
|
|
288
450
|
if (point === undefined) {
|
|
289
451
|
console.warn(`No snapPoint`)
|
|
290
452
|
return 0
|
|
291
453
|
}
|
|
292
454
|
const pct = point / 100
|
|
293
|
-
const next =
|
|
455
|
+
const next = frameSize - pct * frameSize
|
|
294
456
|
return next
|
|
295
457
|
}
|
|
296
458
|
|
|
@@ -305,4 +467,36 @@ function resisted(y: number, minY: number, maxOverflow = 25) {
|
|
|
305
467
|
return y
|
|
306
468
|
}
|
|
307
469
|
|
|
308
|
-
|
|
470
|
+
type SheetControllerContextValue = {
|
|
471
|
+
disableDrag?: boolean
|
|
472
|
+
open?: boolean
|
|
473
|
+
// hide without "closing" to prevent re-animation when shown again
|
|
474
|
+
hidden?: boolean
|
|
475
|
+
onChangeOpen?: React.Dispatch<React.SetStateAction<boolean>> | ((val: boolean) => void)
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const SheetControllerContext = createContext<SheetControllerContextValue | null>(null)
|
|
479
|
+
|
|
480
|
+
export const SheetController = ({
|
|
481
|
+
children,
|
|
482
|
+
onChangeOpen: onChangeOpenProp,
|
|
483
|
+
...value
|
|
484
|
+
}: Partial<SheetControllerContextValue> & { children?: React.ReactNode }) => {
|
|
485
|
+
const onChangeOpen = useEvent(onChangeOpenProp)
|
|
486
|
+
|
|
487
|
+
const memoValue = useMemo(
|
|
488
|
+
() => ({
|
|
489
|
+
open: value.open,
|
|
490
|
+
hidden: value.hidden,
|
|
491
|
+
disableDrag: value.disableDrag,
|
|
492
|
+
onChangeOpen,
|
|
493
|
+
}),
|
|
494
|
+
[onChangeOpen, value.open, value.hidden, value.disableDrag]
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
return (
|
|
498
|
+
<SheetControllerContext.Provider value={memoValue}>{children}</SheetControllerContext.Provider>
|
|
499
|
+
)
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
export { createSheetScope }
|