@lodev09/react-native-true-sheet 3.10.0 → 3.11.0-beta.0
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/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +11 -4
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +10 -5
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetStackManager.kt +2 -5
- package/lib/module/TrueSheet.js +3 -0
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheet.web.js +604 -376
- package/lib/module/TrueSheet.web.js.map +1 -1
- package/lib/module/TrueSheetProvider.js.map +1 -1
- package/lib/module/TrueSheetProvider.web.js +141 -100
- package/lib/module/TrueSheetProvider.web.js.map +1 -1
- package/lib/module/mocks/index.js.map +1 -1
- package/lib/module/reanimated/ReanimatedTrueSheet.web.js.map +1 -1
- package/lib/module/reanimated/useReanimatedPositionChangeHandler.web.js +2 -3
- package/lib/module/reanimated/useReanimatedPositionChangeHandler.web.js.map +1 -1
- package/lib/module/web/constants.js +16 -0
- package/lib/module/web/constants.js.map +1 -0
- package/lib/module/web/vaul/README.md +9 -0
- package/lib/module/web/vaul/browser.js +30 -0
- package/lib/module/web/vaul/browser.js.map +1 -0
- package/lib/module/web/vaul/constants.js +14 -0
- package/lib/module/web/vaul/constants.js.map +1 -0
- package/lib/module/web/vaul/context.js +57 -0
- package/lib/module/web/vaul/context.js.map +1 -0
- package/lib/module/web/vaul/helpers.js +93 -0
- package/lib/module/web/vaul/helpers.js.map +1 -0
- package/lib/module/web/vaul/index.js +1206 -0
- package/lib/module/web/vaul/index.js.map +1 -0
- package/lib/module/web/vaul/style.css +264 -0
- package/lib/module/web/vaul/types.js +2 -0
- package/lib/module/web/vaul/types.js.map +1 -0
- package/lib/module/web/vaul/use-composed-refs.js +34 -0
- package/lib/module/web/vaul/use-composed-refs.js.map +1 -0
- package/lib/module/web/vaul/use-controllable-state.js +54 -0
- package/lib/module/web/vaul/use-controllable-state.js.map +1 -0
- package/lib/module/web/vaul/use-position-fixed.js +123 -0
- package/lib/module/web/vaul/use-position-fixed.js.map +1 -0
- package/lib/module/web/vaul/use-prevent-scroll.js +258 -0
- package/lib/module/web/vaul/use-prevent-scroll.js.map +1 -0
- package/lib/module/web/vaul/use-scale-background.js +57 -0
- package/lib/module/web/vaul/use-scale-background.js.map +1 -0
- package/lib/module/web/vaul/use-snap-points.js +294 -0
- package/lib/module/web/vaul/use-snap-points.js.map +1 -0
- package/lib/typescript/src/TrueSheet.d.ts +2 -2
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.types.d.ts +79 -36
- package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.web.d.ts +3 -11
- package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheetProvider.d.ts +2 -2
- package/lib/typescript/src/TrueSheetProvider.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheetProvider.web.d.ts +17 -14
- package/lib/typescript/src/TrueSheetProvider.web.d.ts.map +1 -1
- package/lib/typescript/src/mocks/index.d.ts +1 -2
- package/lib/typescript/src/mocks/index.d.ts.map +1 -1
- package/lib/typescript/src/navigation/types.d.ts +1 -1
- package/lib/typescript/src/navigation/types.d.ts.map +1 -1
- package/lib/typescript/src/reanimated/ReanimatedTrueSheet.web.d.ts +0 -1
- package/lib/typescript/src/reanimated/ReanimatedTrueSheet.web.d.ts.map +1 -1
- package/lib/typescript/src/reanimated/useReanimatedPositionChangeHandler.web.d.ts +2 -3
- package/lib/typescript/src/reanimated/useReanimatedPositionChangeHandler.web.d.ts.map +1 -1
- package/lib/typescript/src/web/constants.d.ts +14 -0
- package/lib/typescript/src/web/constants.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/browser.d.ts +8 -0
- package/lib/typescript/src/web/vaul/browser.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/constants.d.ts +12 -0
- package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/context.d.ts +45 -0
- package/lib/typescript/src/web/vaul/context.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/helpers.d.ts +17 -0
- package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/index.d.ts +210 -0
- package/lib/typescript/src/web/vaul/index.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/types.d.ts +7 -0
- package/lib/typescript/src/web/vaul/types.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/use-composed-refs.d.ts +14 -0
- package/lib/typescript/src/web/vaul/use-composed-refs.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/use-controllable-state.d.ts +9 -0
- package/lib/typescript/src/web/vaul/use-controllable-state.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/use-position-fixed.d.ts +19 -0
- package/lib/typescript/src/web/vaul/use-position-fixed.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/use-prevent-scroll.d.ts +18 -0
- package/lib/typescript/src/web/vaul/use-prevent-scroll.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/use-scale-background.d.ts +2 -0
- package/lib/typescript/src/web/vaul/use-scale-background.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/use-snap-points.d.ts +37 -0
- package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/TrueSheet.tsx +9 -1
- package/src/TrueSheet.types.ts +88 -38
- package/src/TrueSheet.web.tsx +675 -450
- package/src/TrueSheetProvider.tsx +2 -4
- package/src/TrueSheetProvider.web.tsx +193 -125
- package/src/mocks/index.ts +1 -2
- package/src/navigation/types.ts +0 -1
- package/src/reanimated/ReanimatedTrueSheet.web.tsx +0 -1
- package/src/reanimated/useReanimatedPositionChangeHandler.web.ts +2 -3
- package/src/web/constants.ts +13 -0
- package/src/web/vaul/README.md +9 -0
- package/src/web/vaul/browser.ts +38 -0
- package/src/web/vaul/constants.ts +18 -0
- package/src/web/vaul/context.ts +87 -0
- package/src/web/vaul/helpers.ts +124 -0
- package/src/web/vaul/index.tsx +1590 -0
- package/src/web/vaul/style.css +264 -0
- package/src/web/vaul/types.ts +7 -0
- package/src/web/vaul/use-composed-refs.ts +35 -0
- package/src/web/vaul/use-controllable-state.ts +66 -0
- package/src/web/vaul/use-position-fixed.ts +147 -0
- package/src/web/vaul/use-prevent-scroll.ts +309 -0
- package/src/web/vaul/use-scale-background.ts +64 -0
- package/src/web/vaul/use-snap-points.ts +433 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
[data-vaul-drawer] {
|
|
2
|
+
touch-action: none;
|
|
3
|
+
will-change: transform;
|
|
4
|
+
transition: transform 0.5s cubic-bezier(0.32, 0.72, 0, 1);
|
|
5
|
+
animation-duration: 0.5s;
|
|
6
|
+
animation-timing-function: cubic-bezier(0.32, 0.72, 0, 1);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
[data-vaul-drawer][data-vaul-snap-points='false'][data-vaul-drawer-direction='bottom'][data-state='open'] {
|
|
10
|
+
animation-name: slideFromBottom;
|
|
11
|
+
}
|
|
12
|
+
[data-vaul-drawer][data-vaul-snap-points='false'][data-vaul-drawer-direction='bottom'][data-state='closed'] {
|
|
13
|
+
animation-name: slideToBottom;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Drawers always dismiss by sliding the wrapper off-screen, so we don't want
|
|
17
|
+
vaul's default `slideTo*` drawer keyframes (they'd stack with the wrapper
|
|
18
|
+
translate and look twice as fast). Override with a no-op animation of
|
|
19
|
+
matching duration so Radix Presence still waits for it before unmounting.
|
|
20
|
+
Specificity needs to beat the 4-attribute `slideTo*` selectors above. */
|
|
21
|
+
[data-vaul-drawer][data-vaul-drawer-direction='bottom'][data-vaul-animate='true'][data-state='closed'] {
|
|
22
|
+
animation-name: vaulHold;
|
|
23
|
+
animation-duration: 0.5s;
|
|
24
|
+
}
|
|
25
|
+
@keyframes vaulHold {
|
|
26
|
+
from {
|
|
27
|
+
opacity: 1;
|
|
28
|
+
}
|
|
29
|
+
to {
|
|
30
|
+
opacity: 1;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
[data-vaul-drawer][data-vaul-snap-points='false'][data-vaul-drawer-direction='top'][data-state='open'] {
|
|
35
|
+
animation-name: slideFromTop;
|
|
36
|
+
}
|
|
37
|
+
[data-vaul-drawer][data-vaul-snap-points='false'][data-vaul-drawer-direction='top'][data-state='closed'] {
|
|
38
|
+
animation-name: slideToTop;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
[data-vaul-drawer][data-vaul-snap-points='false'][data-vaul-drawer-direction='left'][data-state='open'] {
|
|
42
|
+
animation-name: slideFromLeft;
|
|
43
|
+
}
|
|
44
|
+
[data-vaul-drawer][data-vaul-snap-points='false'][data-vaul-drawer-direction='left'][data-state='closed'] {
|
|
45
|
+
animation-name: slideToLeft;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
[data-vaul-drawer][data-vaul-snap-points='false'][data-vaul-drawer-direction='right'][data-state='open'] {
|
|
49
|
+
animation-name: slideFromRight;
|
|
50
|
+
}
|
|
51
|
+
[data-vaul-drawer][data-vaul-snap-points='false'][data-vaul-drawer-direction='right'][data-state='closed'] {
|
|
52
|
+
animation-name: slideToRight;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
[data-vaul-drawer][data-vaul-snap-points='true'][data-vaul-drawer-direction='bottom'] {
|
|
56
|
+
transform: translate3d(0, var(--initial-transform, 100%), 0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
[data-vaul-drawer][data-vaul-snap-points='true'][data-vaul-drawer-direction='top'] {
|
|
60
|
+
transform: translate3d(0, calc(var(--initial-transform, 100%) * -1), 0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
[data-vaul-drawer][data-vaul-snap-points='true'][data-vaul-drawer-direction='left'] {
|
|
64
|
+
transform: translate3d(calc(var(--initial-transform, 100%) * -1), 0, 0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
[data-vaul-drawer][data-vaul-snap-points='true'][data-vaul-drawer-direction='right'] {
|
|
68
|
+
transform: translate3d(var(--initial-transform, 100%), 0, 0);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
[data-vaul-drawer][data-vaul-delayed-snap-points='true'][data-vaul-drawer-direction='top'] {
|
|
72
|
+
transform: translate3d(0, var(--snap-point-height, 0), 0);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
[data-vaul-drawer][data-vaul-delayed-snap-points='true'][data-vaul-drawer-direction='bottom'] {
|
|
76
|
+
transform: translate3d(0, var(--snap-point-height, 0), 0);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
[data-vaul-drawer][data-vaul-delayed-snap-points='true'][data-vaul-drawer-direction='left'] {
|
|
80
|
+
transform: translate3d(var(--snap-point-height, 0), 0, 0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
[data-vaul-drawer][data-vaul-delayed-snap-points='true'][data-vaul-drawer-direction='right'] {
|
|
84
|
+
transform: translate3d(var(--snap-point-height, 0), 0, 0);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
[data-vaul-overlay][data-vaul-snap-points='false'] {
|
|
88
|
+
animation-duration: 0.5s;
|
|
89
|
+
animation-timing-function: cubic-bezier(0.32, 0.72, 0, 1);
|
|
90
|
+
}
|
|
91
|
+
[data-vaul-overlay][data-vaul-snap-points='false'][data-state='open'] {
|
|
92
|
+
animation-name: fadeIn;
|
|
93
|
+
}
|
|
94
|
+
[data-vaul-overlay][data-state='closed'] {
|
|
95
|
+
animation-name: fadeOut;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
[data-vaul-animate='false'] {
|
|
99
|
+
animation: none !important;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
[data-vaul-overlay][data-vaul-snap-points='true'] {
|
|
103
|
+
opacity: 0;
|
|
104
|
+
transition: opacity 0.5s cubic-bezier(0.32, 0.72, 0, 1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
[data-vaul-drawer]:not([data-vaul-custom-container='true'])::after {
|
|
108
|
+
content: '';
|
|
109
|
+
position: absolute;
|
|
110
|
+
background: inherit;
|
|
111
|
+
background-color: inherit;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
[data-vaul-drawer][data-vaul-drawer-direction='top']::after {
|
|
115
|
+
top: initial;
|
|
116
|
+
bottom: 100%;
|
|
117
|
+
left: 0;
|
|
118
|
+
right: 0;
|
|
119
|
+
height: 200%;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
[data-vaul-drawer][data-vaul-drawer-direction='bottom']::after {
|
|
123
|
+
top: 100%;
|
|
124
|
+
bottom: initial;
|
|
125
|
+
left: 0;
|
|
126
|
+
right: 0;
|
|
127
|
+
height: 200%;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
[data-vaul-drawer][data-vaul-drawer-direction='left']::after {
|
|
131
|
+
left: initial;
|
|
132
|
+
right: 100%;
|
|
133
|
+
top: 0;
|
|
134
|
+
bottom: 0;
|
|
135
|
+
width: 200%;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
[data-vaul-drawer][data-vaul-drawer-direction='right']::after {
|
|
139
|
+
left: 100%;
|
|
140
|
+
right: initial;
|
|
141
|
+
top: 0;
|
|
142
|
+
bottom: 0;
|
|
143
|
+
width: 200%;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
[data-vaul-overlay][data-vaul-snap-points='true'][data-vaul-delayed-snap-points='true'][data-vaul-snap-points-overlay='true'] {
|
|
147
|
+
opacity: 1;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
[data-vaul-handle] {
|
|
151
|
+
display: block;
|
|
152
|
+
position: relative;
|
|
153
|
+
opacity: 0.7;
|
|
154
|
+
background: #e2e2e4;
|
|
155
|
+
margin-left: auto;
|
|
156
|
+
margin-right: auto;
|
|
157
|
+
height: 5px;
|
|
158
|
+
width: 32px;
|
|
159
|
+
border-radius: 1rem;
|
|
160
|
+
touch-action: pan-y;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
[data-vaul-handle]:hover,
|
|
164
|
+
[data-vaul-handle]:active {
|
|
165
|
+
opacity: 1;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
[data-vaul-handle-hitarea] {
|
|
169
|
+
position: absolute;
|
|
170
|
+
left: 50%;
|
|
171
|
+
top: 50%;
|
|
172
|
+
transform: translate(-50%, -50%);
|
|
173
|
+
width: max(100%, 2.75rem); /* 44px */
|
|
174
|
+
height: max(100%, 2.75rem); /* 44px */
|
|
175
|
+
touch-action: inherit;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@media (hover: hover) and (pointer: fine) {
|
|
179
|
+
[data-vaul-drawer] {
|
|
180
|
+
user-select: none;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@media (pointer: fine) {
|
|
185
|
+
[data-vaul-handle-hitarea]: {
|
|
186
|
+
width: 100%;
|
|
187
|
+
height: 100%;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@keyframes fadeIn {
|
|
192
|
+
from {
|
|
193
|
+
opacity: 0;
|
|
194
|
+
}
|
|
195
|
+
to {
|
|
196
|
+
opacity: 1;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
@keyframes fadeOut {
|
|
201
|
+
to {
|
|
202
|
+
opacity: 0;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
@keyframes slideFromBottom {
|
|
207
|
+
from {
|
|
208
|
+
transform: translate3d(0, var(--initial-transform, 100%), 0);
|
|
209
|
+
}
|
|
210
|
+
to {
|
|
211
|
+
transform: translate3d(0, 0, 0);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
@keyframes slideToBottom {
|
|
216
|
+
to {
|
|
217
|
+
transform: translate3d(0, var(--initial-transform, 100%), 0);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
@keyframes slideFromTop {
|
|
222
|
+
from {
|
|
223
|
+
transform: translate3d(0, calc(var(--initial-transform, 100%) * -1), 0);
|
|
224
|
+
}
|
|
225
|
+
to {
|
|
226
|
+
transform: translate3d(0, 0, 0);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
@keyframes slideToTop {
|
|
231
|
+
to {
|
|
232
|
+
transform: translate3d(0, calc(var(--initial-transform, 100%) * -1), 0);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
@keyframes slideFromLeft {
|
|
237
|
+
from {
|
|
238
|
+
transform: translate3d(calc(var(--initial-transform, 100%) * -1), 0, 0);
|
|
239
|
+
}
|
|
240
|
+
to {
|
|
241
|
+
transform: translate3d(0, 0, 0);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
@keyframes slideToLeft {
|
|
246
|
+
to {
|
|
247
|
+
transform: translate3d(calc(var(--initial-transform, 100%) * -1), 0, 0);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
@keyframes slideFromRight {
|
|
252
|
+
from {
|
|
253
|
+
transform: translate3d(var(--initial-transform, 100%), 0, 0);
|
|
254
|
+
}
|
|
255
|
+
to {
|
|
256
|
+
transform: translate3d(0, 0, 0);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
@keyframes slideToRight {
|
|
261
|
+
to {
|
|
262
|
+
transform: translate3d(var(--initial-transform, 100%), 0, 0);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// This code comes from https://github.com/radix-ui/primitives/tree/main/packages/react/compose-refs
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
type PossibleRef<T> = React.Ref<T> | undefined;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Set a given ref to a given value
|
|
9
|
+
* This utility takes care of different types of refs: callback refs and RefObject(s)
|
|
10
|
+
*/
|
|
11
|
+
function setRef<T>(ref: PossibleRef<T>, value: T) {
|
|
12
|
+
if (typeof ref === 'function') {
|
|
13
|
+
ref(value);
|
|
14
|
+
} else if (ref !== null && ref !== undefined) {
|
|
15
|
+
(ref as React.MutableRefObject<T>).current = value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A utility to compose multiple refs together
|
|
21
|
+
* Accepts callback refs and RefObject(s)
|
|
22
|
+
*/
|
|
23
|
+
function composeRefs<T>(...refs: PossibleRef<T>[]) {
|
|
24
|
+
return (node: T) => refs.forEach((ref) => setRef(ref, node));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A custom hook that composes multiple refs
|
|
29
|
+
* Accepts callback refs and RefObject(s)
|
|
30
|
+
*/
|
|
31
|
+
function useComposedRefs<T>(...refs: PossibleRef<T>[]) {
|
|
32
|
+
return React.useCallback(composeRefs(...refs), refs);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { composeRefs, useComposedRefs };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// This code comes from https://github.com/radix-ui/primitives/blob/main/packages/react/use-controllable-state/src/useControllableState.tsx
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
type UseControllableStateParams<T> = {
|
|
6
|
+
prop?: T | undefined;
|
|
7
|
+
defaultProp?: T | undefined;
|
|
8
|
+
onChange?: (state: T) => void;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type SetStateFn<T> = (prevState?: T) => T;
|
|
12
|
+
|
|
13
|
+
function useCallbackRef<T extends (...args: any[]) => any>(callback: T | undefined): T {
|
|
14
|
+
const callbackRef = React.useRef(callback);
|
|
15
|
+
|
|
16
|
+
React.useEffect(() => {
|
|
17
|
+
callbackRef.current = callback;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// https://github.com/facebook/react/issues/19240
|
|
21
|
+
return React.useMemo(() => ((...args) => callbackRef.current?.(...args)) as T, []);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function useUncontrolledState<T>({
|
|
25
|
+
defaultProp,
|
|
26
|
+
onChange,
|
|
27
|
+
}: Omit<UseControllableStateParams<T>, 'prop'>) {
|
|
28
|
+
const uncontrolledState = React.useState<T | undefined>(defaultProp);
|
|
29
|
+
const [value] = uncontrolledState;
|
|
30
|
+
const prevValueRef = React.useRef(value);
|
|
31
|
+
const handleChange = useCallbackRef(onChange);
|
|
32
|
+
|
|
33
|
+
React.useEffect(() => {
|
|
34
|
+
if (prevValueRef.current !== value) {
|
|
35
|
+
handleChange(value as T);
|
|
36
|
+
prevValueRef.current = value;
|
|
37
|
+
}
|
|
38
|
+
}, [value, prevValueRef, handleChange]);
|
|
39
|
+
|
|
40
|
+
return uncontrolledState;
|
|
41
|
+
}
|
|
42
|
+
export function useControllableState<T>({
|
|
43
|
+
prop,
|
|
44
|
+
defaultProp,
|
|
45
|
+
onChange = () => {},
|
|
46
|
+
}: UseControllableStateParams<T>) {
|
|
47
|
+
const [uncontrolledProp, setUncontrolledProp] = useUncontrolledState({ defaultProp, onChange });
|
|
48
|
+
const isControlled = prop !== undefined;
|
|
49
|
+
const value = isControlled ? prop : uncontrolledProp;
|
|
50
|
+
const handleChange = useCallbackRef(onChange);
|
|
51
|
+
|
|
52
|
+
const setValue: React.Dispatch<React.SetStateAction<T | undefined>> = React.useCallback(
|
|
53
|
+
(nextValue) => {
|
|
54
|
+
if (isControlled) {
|
|
55
|
+
const setter = nextValue as SetStateFn<T>;
|
|
56
|
+
const next = typeof nextValue === 'function' ? setter(prop) : nextValue;
|
|
57
|
+
if (next !== prop) handleChange(next as T);
|
|
58
|
+
} else {
|
|
59
|
+
setUncontrolledProp(nextValue);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
[isControlled, prop, setUncontrolledProp, handleChange]
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
return [value, setValue] as const;
|
|
66
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { isSafari } from './browser';
|
|
3
|
+
|
|
4
|
+
let previousBodyPosition: Record<string, string> | null = null;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* This hook is necessary to prevent buggy behavior on iOS devices (need to test on Android).
|
|
8
|
+
* I won't get into too much detail about what bugs it solves, but so far I've found that setting the body to `position: fixed` is the most reliable way to prevent those bugs.
|
|
9
|
+
* Issues that this hook solves:
|
|
10
|
+
* https://github.com/emilkowalski/vaul/issues/435
|
|
11
|
+
* https://github.com/emilkowalski/vaul/issues/433
|
|
12
|
+
* And more that I discovered, but were just not reported.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export function usePositionFixed({
|
|
16
|
+
isOpen,
|
|
17
|
+
modal,
|
|
18
|
+
nested,
|
|
19
|
+
hasBeenOpened,
|
|
20
|
+
preventScrollRestoration,
|
|
21
|
+
noBodyStyles,
|
|
22
|
+
}: {
|
|
23
|
+
isOpen: boolean;
|
|
24
|
+
modal: boolean;
|
|
25
|
+
nested: boolean;
|
|
26
|
+
hasBeenOpened: boolean;
|
|
27
|
+
preventScrollRestoration: boolean;
|
|
28
|
+
noBodyStyles: boolean;
|
|
29
|
+
}) {
|
|
30
|
+
const [activeUrl, setActiveUrl] = React.useState(() =>
|
|
31
|
+
typeof window !== 'undefined' ? window.location.href : ''
|
|
32
|
+
);
|
|
33
|
+
const scrollPos = React.useRef(0);
|
|
34
|
+
|
|
35
|
+
const setPositionFixed = React.useCallback(() => {
|
|
36
|
+
// All browsers on iOS will return true here.
|
|
37
|
+
if (!isSafari()) return;
|
|
38
|
+
|
|
39
|
+
// If previousBodyPosition is already set, don't set it again.
|
|
40
|
+
if (previousBodyPosition === null && isOpen && !noBodyStyles) {
|
|
41
|
+
previousBodyPosition = {
|
|
42
|
+
position: document.body.style.position,
|
|
43
|
+
top: document.body.style.top,
|
|
44
|
+
left: document.body.style.left,
|
|
45
|
+
height: document.body.style.height,
|
|
46
|
+
right: 'unset',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Update the dom inside an animation frame
|
|
50
|
+
const { scrollX, innerHeight } = window;
|
|
51
|
+
|
|
52
|
+
document.body.style.setProperty('position', 'fixed', 'important');
|
|
53
|
+
Object.assign(document.body.style, {
|
|
54
|
+
top: `${-scrollPos.current}px`,
|
|
55
|
+
left: `${-scrollX}px`,
|
|
56
|
+
right: '0px',
|
|
57
|
+
height: 'auto',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
window.setTimeout(
|
|
61
|
+
() =>
|
|
62
|
+
window.requestAnimationFrame(() => {
|
|
63
|
+
// Attempt to check if the bottom bar appeared due to the position change
|
|
64
|
+
const bottomBarHeight = innerHeight - window.innerHeight;
|
|
65
|
+
if (bottomBarHeight && scrollPos.current >= innerHeight) {
|
|
66
|
+
// Move the content further up so that the bottom bar doesn't hide it
|
|
67
|
+
document.body.style.top = `${-(scrollPos.current + bottomBarHeight)}px`;
|
|
68
|
+
}
|
|
69
|
+
}),
|
|
70
|
+
300
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}, [isOpen]);
|
|
74
|
+
|
|
75
|
+
const restorePositionSetting = React.useCallback(() => {
|
|
76
|
+
// All browsers on iOS will return true here.
|
|
77
|
+
if (!isSafari()) return;
|
|
78
|
+
|
|
79
|
+
if (previousBodyPosition !== null && !noBodyStyles) {
|
|
80
|
+
// Convert the position from "px" to Int
|
|
81
|
+
const y = -parseInt(document.body.style.top, 10);
|
|
82
|
+
const x = -parseInt(document.body.style.left, 10);
|
|
83
|
+
|
|
84
|
+
// Restore styles
|
|
85
|
+
Object.assign(document.body.style, previousBodyPosition);
|
|
86
|
+
|
|
87
|
+
window.requestAnimationFrame(() => {
|
|
88
|
+
if (preventScrollRestoration && activeUrl !== window.location.href) {
|
|
89
|
+
setActiveUrl(window.location.href);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
window.scrollTo(x, y);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
previousBodyPosition = null;
|
|
97
|
+
}
|
|
98
|
+
}, [activeUrl]);
|
|
99
|
+
|
|
100
|
+
React.useEffect(() => {
|
|
101
|
+
function onScroll() {
|
|
102
|
+
scrollPos.current = window.scrollY;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
onScroll();
|
|
106
|
+
|
|
107
|
+
window.addEventListener('scroll', onScroll);
|
|
108
|
+
|
|
109
|
+
return () => {
|
|
110
|
+
window.removeEventListener('scroll', onScroll);
|
|
111
|
+
};
|
|
112
|
+
}, []);
|
|
113
|
+
|
|
114
|
+
React.useEffect(() => {
|
|
115
|
+
if (!modal) return;
|
|
116
|
+
|
|
117
|
+
return () => {
|
|
118
|
+
if (typeof document === 'undefined') return;
|
|
119
|
+
|
|
120
|
+
// Another drawer is opened, safe to ignore the execution
|
|
121
|
+
const hasDrawerOpened = !!document.querySelector('[data-vaul-drawer]');
|
|
122
|
+
if (hasDrawerOpened) return;
|
|
123
|
+
|
|
124
|
+
restorePositionSetting();
|
|
125
|
+
};
|
|
126
|
+
}, [modal, restorePositionSetting]);
|
|
127
|
+
|
|
128
|
+
React.useEffect(() => {
|
|
129
|
+
if (nested || !hasBeenOpened) return;
|
|
130
|
+
// This is needed to force Safari toolbar to show **before** the drawer starts animating to prevent a gnarly shift from happening
|
|
131
|
+
if (isOpen) {
|
|
132
|
+
// avoid for standalone mode (PWA)
|
|
133
|
+
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
|
|
134
|
+
!isStandalone && setPositionFixed();
|
|
135
|
+
|
|
136
|
+
if (!modal) {
|
|
137
|
+
window.setTimeout(() => {
|
|
138
|
+
restorePositionSetting();
|
|
139
|
+
}, 500);
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
restorePositionSetting();
|
|
143
|
+
}
|
|
144
|
+
}, [isOpen, hasBeenOpened, activeUrl, modal, nested, setPositionFixed, restorePositionSetting]);
|
|
145
|
+
|
|
146
|
+
return { restorePositionSetting };
|
|
147
|
+
}
|