@eventlook/sdk 1.4.49-beta.5 → 1.4.49-beta.7
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/form/PaymentOverviewBox.js +5 -4
- package/dist/cjs/form/PaymentOverviewBox.js.map +1 -1
- package/dist/cjs/form/PaymentOverviewDrawer.js +177 -109
- package/dist/cjs/form/PaymentOverviewDrawer.js.map +1 -1
- package/dist/cjs/form/TicketForm.js +5 -8
- package/dist/cjs/form/TicketForm.js.map +1 -1
- package/dist/cjs/form/merchandise/MerchandiseSlider.js +1 -1
- package/dist/cjs/form/merchandise/MerchandiseSlider.js.map +1 -1
- package/dist/cjs/form/product/ProductVariantsDialog.js +2 -2
- package/dist/cjs/form/product/ProductVariantsDialog.js.map +1 -1
- package/dist/cjs/form/style.js +5 -0
- package/dist/cjs/form/style.js.map +1 -1
- package/dist/cjs/form/tickets/TicketQuantityControl.js +5 -3
- package/dist/cjs/form/tickets/TicketQuantityControl.js.map +1 -1
- package/dist/cjs/hooks/useScrollToFirstError.js +19 -10
- package/dist/cjs/hooks/useScrollToFirstError.js.map +1 -1
- package/dist/cjs/locales/cs.js +4 -0
- package/dist/cjs/locales/cs.js.map +1 -1
- package/dist/cjs/locales/en.js +4 -0
- package/dist/cjs/locales/en.js.map +1 -1
- package/dist/cjs/locales/es.js +4 -0
- package/dist/cjs/locales/es.js.map +1 -1
- package/dist/cjs/locales/pl.js +4 -0
- package/dist/cjs/locales/pl.js.map +1 -1
- package/dist/cjs/locales/sk.js +4 -0
- package/dist/cjs/locales/sk.js.map +1 -1
- package/dist/cjs/locales/uk.js +4 -0
- package/dist/cjs/locales/uk.js.map +1 -1
- package/dist/esm/form/PaymentOverviewBox.js +5 -4
- package/dist/esm/form/PaymentOverviewBox.js.map +1 -1
- package/dist/esm/form/PaymentOverviewDrawer.js +177 -109
- package/dist/esm/form/PaymentOverviewDrawer.js.map +1 -1
- package/dist/esm/form/TicketForm.js +6 -9
- package/dist/esm/form/TicketForm.js.map +1 -1
- package/dist/esm/form/merchandise/MerchandiseSlider.js +1 -1
- package/dist/esm/form/merchandise/MerchandiseSlider.js.map +1 -1
- package/dist/esm/form/product/ProductVariantsDialog.js +2 -2
- package/dist/esm/form/product/ProductVariantsDialog.js.map +1 -1
- package/dist/esm/form/style.js +5 -0
- package/dist/esm/form/style.js.map +1 -1
- package/dist/esm/form/tickets/TicketQuantityControl.js +5 -3
- package/dist/esm/form/tickets/TicketQuantityControl.js.map +1 -1
- package/dist/esm/hooks/useScrollToFirstError.js +19 -10
- package/dist/esm/hooks/useScrollToFirstError.js.map +1 -1
- package/dist/esm/locales/cs.js +4 -0
- package/dist/esm/locales/cs.js.map +1 -1
- package/dist/esm/locales/en.js +4 -0
- package/dist/esm/locales/en.js.map +1 -1
- package/dist/esm/locales/es.js +4 -0
- package/dist/esm/locales/es.js.map +1 -1
- package/dist/esm/locales/pl.js +4 -0
- package/dist/esm/locales/pl.js.map +1 -1
- package/dist/esm/locales/sk.js +4 -0
- package/dist/esm/locales/sk.js.map +1 -1
- package/dist/esm/locales/uk.js +4 -0
- package/dist/esm/locales/uk.js.map +1 -1
- package/package.json +1 -3
- package/rollup.config.mjs +0 -1
- package/src/form/PaymentOverviewBox.tsx +19 -13
- package/src/form/PaymentOverviewDrawer.tsx +238 -128
- package/src/form/TicketForm.tsx +7 -8
- package/src/form/merchandise/MerchandiseSlider.tsx +1 -1
- package/src/form/product/ProductVariantsDialog.tsx +2 -0
- package/src/form/style.ts +6 -0
- package/src/form/tickets/TicketQuantityControl.tsx +6 -0
- package/src/hooks/useScrollToFirstError.ts +22 -17
- package/src/locales/cs.tsx +4 -0
- package/src/locales/en.tsx +4 -0
- package/src/locales/es.tsx +4 -0
- package/src/locales/pl.tsx +4 -0
- package/src/locales/sk.tsx +4 -0
- package/src/locales/uk.tsx +4 -0
- package/src/hooks/useConsentScrollOnDrawerOpen.ts +0 -73
|
@@ -10,60 +10,92 @@ import { EVENTLOOK_ORDER_FORM_ID } from '@utils/data/global.ts';
|
|
|
10
10
|
interface Props {
|
|
11
11
|
event: IEvent;
|
|
12
12
|
totalPrice: number;
|
|
13
|
+
termsAndConditionsRef?: React.RefObject<HTMLElement | null>;
|
|
13
14
|
onOpenChange?: (open: boolean) => void;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
const swipeAreaWidth = 120;
|
|
17
18
|
|
|
18
|
-
const
|
|
19
|
+
const clearResetPulledPastTimeout = (timeoutRef: { current: number | null }) => {
|
|
20
|
+
if (timeoutRef.current !== null) {
|
|
21
|
+
window.clearTimeout(timeoutRef.current);
|
|
22
|
+
timeoutRef.current = null;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const PaymentOverviewDrawer: React.FC<Props> = ({
|
|
27
|
+
event,
|
|
28
|
+
totalPrice,
|
|
29
|
+
termsAndConditionsRef,
|
|
30
|
+
onOpenChange,
|
|
31
|
+
}) => {
|
|
19
32
|
const { t, lang } = useGlobal();
|
|
20
|
-
const [
|
|
33
|
+
const [isDrawerOpened, setIsDrawerOpened] = useState(false);
|
|
21
34
|
const [isOpeningGesture, setIsOpeningGesture] = useState(false);
|
|
22
35
|
const [hasPulledPastClosedTabHeight, setHasPulledPastClosedTabHeight] = useState(false);
|
|
36
|
+
const [isDrawerHidden, setIsDrawerHidden] = useState(false);
|
|
23
37
|
const [closedTabHeight, setClosedTabHeight] = useState(0);
|
|
24
38
|
const openingTouchStartYRef = useRef<number | null>(null);
|
|
25
39
|
const openDrawerRef = useRef(false);
|
|
40
|
+
const isOpeningGestureRef = useRef(false);
|
|
41
|
+
const shouldReopenOnScrollUpRef = useRef(false);
|
|
42
|
+
const lastScrollYRef = useRef(0);
|
|
43
|
+
const isScrollingDownRef = useRef(false);
|
|
44
|
+
const wasAtPageBottomRef = useRef(false);
|
|
26
45
|
const resetPulledPastTimeoutRef = useRef<number | null>(null);
|
|
27
46
|
const closedTabRef = useRef<HTMLDivElement>(null);
|
|
28
47
|
|
|
29
|
-
|
|
30
|
-
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
openDrawerRef.current = isDrawerOpened;
|
|
50
|
+
}, [isDrawerOpened]);
|
|
31
51
|
|
|
32
|
-
// Opens drawer when scrolling down
|
|
33
52
|
useEffect(() => {
|
|
34
|
-
|
|
35
|
-
|
|
53
|
+
isOpeningGestureRef.current = isOpeningGesture;
|
|
54
|
+
}, [isOpeningGesture]);
|
|
36
55
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const isScrollingDown = currentY < prevYRef.current;
|
|
56
|
+
// reopens the drawer when user scrolls up and the drawer was closed by scrolling down to the bottom of the page
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (typeof window === 'undefined') {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
43
61
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
62
|
+
const atBottomThreshold = 8;
|
|
63
|
+
lastScrollYRef.current = window.scrollY;
|
|
64
|
+
isScrollingDownRef.current = false;
|
|
65
|
+
wasAtPageBottomRef.current =
|
|
66
|
+
window.innerHeight + window.scrollY >=
|
|
67
|
+
document.documentElement.scrollHeight - atBottomThreshold;
|
|
47
68
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
69
|
+
const handleScroll = () => {
|
|
70
|
+
const currentY = window.scrollY;
|
|
71
|
+
const isScrollingUp = currentY < lastScrollYRef.current;
|
|
72
|
+
const isScrollingDown = currentY > lastScrollYRef.current;
|
|
73
|
+
const isAtPageBottom =
|
|
74
|
+
window.innerHeight + currentY >= document.documentElement.scrollHeight - atBottomThreshold;
|
|
75
|
+
|
|
76
|
+
isScrollingDownRef.current = isScrollingDown;
|
|
77
|
+
|
|
78
|
+
if (
|
|
79
|
+
isScrollingUp &&
|
|
80
|
+
wasAtPageBottomRef.current &&
|
|
81
|
+
shouldReopenOnScrollUpRef.current &&
|
|
82
|
+
!openDrawerRef.current
|
|
83
|
+
) {
|
|
84
|
+
shouldReopenOnScrollUpRef.current = false;
|
|
85
|
+
setIsDrawerHidden(false);
|
|
86
|
+
openDrawerHandler();
|
|
54
87
|
}
|
|
55
|
-
);
|
|
56
88
|
|
|
57
|
-
|
|
89
|
+
wasAtPageBottomRef.current = isAtPageBottom;
|
|
90
|
+
lastScrollYRef.current = currentY;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
58
94
|
|
|
59
95
|
return () => {
|
|
60
|
-
|
|
96
|
+
window.removeEventListener('scroll', handleScroll);
|
|
61
97
|
};
|
|
62
|
-
}, [
|
|
63
|
-
|
|
64
|
-
useEffect(() => {
|
|
65
|
-
openDrawerRef.current = openDrawer;
|
|
66
|
-
}, [openDrawer]);
|
|
98
|
+
}, []);
|
|
67
99
|
|
|
68
100
|
useEffect(() => {
|
|
69
101
|
const closedTab = closedTabRef.current;
|
|
@@ -88,50 +120,129 @@ const PaymentOverviewDrawer: React.FC<Props> = ({ event, totalPrice, onOpenChang
|
|
|
88
120
|
|
|
89
121
|
useEffect(
|
|
90
122
|
() => () => {
|
|
91
|
-
|
|
92
|
-
window.clearTimeout(resetPulledPastTimeoutRef.current);
|
|
93
|
-
}
|
|
123
|
+
clearResetPulledPastTimeout(resetPulledPastTimeoutRef);
|
|
94
124
|
},
|
|
95
125
|
[]
|
|
96
126
|
);
|
|
97
127
|
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
const termsAndConditionsElement = termsAndConditionsRef?.current;
|
|
130
|
+
if (!termsAndConditionsElement) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!('IntersectionObserver' in window)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const observer = new IntersectionObserver(
|
|
139
|
+
(entries) => {
|
|
140
|
+
const entry = entries[0];
|
|
141
|
+
const hasReachedTermsSection = entry?.isIntersecting ?? false;
|
|
142
|
+
const shouldAutoCloseDrawer = hasReachedTermsSection;
|
|
143
|
+
const shouldHideDrawerTab =
|
|
144
|
+
hasReachedTermsSection && !openDrawerRef.current && !isOpeningGestureRef.current;
|
|
145
|
+
setIsDrawerHidden(shouldHideDrawerTab);
|
|
146
|
+
|
|
147
|
+
if (shouldAutoCloseDrawer && openDrawerRef.current && isScrollingDownRef.current) {
|
|
148
|
+
closeDrawerHandler({ shouldReopenOnScrollUp: true });
|
|
149
|
+
setIsDrawerHidden(true);
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
root: null,
|
|
154
|
+
threshold: 0,
|
|
155
|
+
rootMargin: '0px 0px -40% 0px',
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
observer.observe(termsAndConditionsElement);
|
|
160
|
+
|
|
161
|
+
return () => {
|
|
162
|
+
observer.disconnect();
|
|
163
|
+
};
|
|
164
|
+
}, [termsAndConditionsRef]);
|
|
165
|
+
|
|
98
166
|
const toggleDrawer = () => {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
167
|
+
if (openDrawerRef.current) {
|
|
168
|
+
closeDrawerHandler();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
openDrawerHandler();
|
|
104
173
|
};
|
|
105
174
|
|
|
106
175
|
const openDrawerHandler = () => {
|
|
107
|
-
|
|
108
|
-
window.clearTimeout(resetPulledPastTimeoutRef.current);
|
|
109
|
-
resetPulledPastTimeoutRef.current = null;
|
|
110
|
-
}
|
|
176
|
+
clearResetPulledPastTimeout(resetPulledPastTimeoutRef);
|
|
111
177
|
setIsOpeningGesture(false);
|
|
112
178
|
setHasPulledPastClosedTabHeight(false);
|
|
113
179
|
openingTouchStartYRef.current = null;
|
|
114
|
-
|
|
180
|
+
shouldReopenOnScrollUpRef.current = false;
|
|
181
|
+
setIsDrawerOpened(true);
|
|
115
182
|
onOpenChange?.(true);
|
|
116
183
|
};
|
|
117
184
|
|
|
118
|
-
const closeDrawerHandler = (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
185
|
+
const closeDrawerHandler = ({
|
|
186
|
+
shouldReopenOnScrollUp = false,
|
|
187
|
+
}: { shouldReopenOnScrollUp?: boolean } = {}) => {
|
|
188
|
+
clearResetPulledPastTimeout(resetPulledPastTimeoutRef);
|
|
123
189
|
setIsOpeningGesture(false);
|
|
124
190
|
setHasPulledPastClosedTabHeight(false);
|
|
125
191
|
openingTouchStartYRef.current = null;
|
|
126
|
-
|
|
192
|
+
shouldReopenOnScrollUpRef.current = shouldReopenOnScrollUp;
|
|
193
|
+
setIsDrawerOpened(false);
|
|
127
194
|
onOpenChange?.(false);
|
|
128
195
|
};
|
|
129
196
|
|
|
197
|
+
const scheduleResetPulledPastTimeout = () => {
|
|
198
|
+
clearResetPulledPastTimeout(resetPulledPastTimeoutRef);
|
|
199
|
+
resetPulledPastTimeoutRef.current = window.setTimeout(() => {
|
|
200
|
+
if (!openDrawerRef.current) {
|
|
201
|
+
setHasPulledPastClosedTabHeight(false);
|
|
202
|
+
}
|
|
203
|
+
}, 180);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const handleGestureStart = (startY: number | null) => {
|
|
207
|
+
openingTouchStartYRef.current = startY;
|
|
208
|
+
setIsOpeningGesture(true);
|
|
209
|
+
setHasPulledPastClosedTabHeight(false);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const handleGestureMove = (currentY?: number) => {
|
|
213
|
+
if (!isOpeningGestureRef.current) return;
|
|
214
|
+
|
|
215
|
+
const startY = openingTouchStartYRef.current;
|
|
216
|
+
if (startY === null || typeof currentY !== 'number') return;
|
|
217
|
+
|
|
218
|
+
const pullDistance = startY - currentY;
|
|
219
|
+
const currentClosedTabHeight = closedTabRef.current?.offsetHeight ?? 0;
|
|
220
|
+
setHasPulledPastClosedTabHeight(
|
|
221
|
+
currentClosedTabHeight > 0 && pullDistance >= currentClosedTabHeight
|
|
222
|
+
);
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const handleGestureCancel = () => {
|
|
226
|
+
openingTouchStartYRef.current = null;
|
|
227
|
+
setIsOpeningGesture(false);
|
|
228
|
+
setHasPulledPastClosedTabHeight(false);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const handleGestureEnd = ({
|
|
232
|
+
shouldResetTouchStart = false,
|
|
233
|
+
}: { shouldResetTouchStart?: boolean } = {}) => {
|
|
234
|
+
if (shouldResetTouchStart) {
|
|
235
|
+
openingTouchStartYRef.current = null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (!isDrawerOpened) {
|
|
239
|
+
setIsOpeningGesture(false);
|
|
240
|
+
scheduleResetPulledPastTimeout();
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
130
244
|
return (
|
|
131
245
|
<Box sx={{ display: { md: 'none' } }}>
|
|
132
|
-
{/* Sentinel element for intersection observer */}
|
|
133
|
-
<Box ref={sentinelRef} sx={{ height: 1, visibility: 'hidden' }} />
|
|
134
|
-
|
|
135
246
|
{/* Drawer Tab */}
|
|
136
247
|
<Box
|
|
137
248
|
ref={closedTabRef}
|
|
@@ -141,43 +252,65 @@ const PaymentOverviewDrawer: React.FC<Props> = ({ event, totalPrice, onOpenChang
|
|
|
141
252
|
left: 0,
|
|
142
253
|
right: 0,
|
|
143
254
|
backgroundColor: (theme) => theme.palette.background.paper,
|
|
144
|
-
borderTopLeftRadius: 16,
|
|
145
|
-
borderTopRightRadius: 16,
|
|
146
|
-
boxShadow:
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
255
|
+
borderTopLeftRadius: isOpeningGesture || isDrawerOpened ? 0 : 16,
|
|
256
|
+
borderTopRightRadius: isOpeningGesture || isDrawerOpened ? 0 : 16,
|
|
257
|
+
boxShadow:
|
|
258
|
+
isOpeningGesture || isDrawerOpened ? 'none' : '0 -8px 10px 2px rgba(0,0,0,0.2)',
|
|
259
|
+
zIndex: (theme) => theme.zIndex.drawer + 1,
|
|
260
|
+
opacity: isDrawerHidden ? 0 : 1,
|
|
261
|
+
transform: isDrawerHidden ? 'translateY(calc(100% + 12px))' : 'translateY(0)',
|
|
262
|
+
transition:
|
|
263
|
+
'opacity 180ms ease, transform 180ms ease, border-radius 120ms ease, box-shadow 120ms ease',
|
|
151
264
|
pointerEvents: 'none',
|
|
152
265
|
}}
|
|
153
266
|
>
|
|
154
|
-
|
|
155
|
-
sx={{
|
|
156
|
-
display: 'flex',
|
|
157
|
-
justifyContent: 'center',
|
|
158
|
-
alignItems: 'center',
|
|
159
|
-
width: '100%',
|
|
160
|
-
py: 1,
|
|
161
|
-
}}
|
|
162
|
-
>
|
|
267
|
+
{!isDrawerOpened && (
|
|
163
268
|
<Box
|
|
164
269
|
sx={{
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
270
|
+
display: 'flex',
|
|
271
|
+
justifyContent: 'center',
|
|
272
|
+
alignItems: 'center',
|
|
273
|
+
width: '100%',
|
|
274
|
+
py: 1,
|
|
169
275
|
}}
|
|
170
|
-
|
|
171
|
-
|
|
276
|
+
>
|
|
277
|
+
<Box
|
|
278
|
+
sx={{
|
|
279
|
+
width: 80,
|
|
280
|
+
height: 6,
|
|
281
|
+
backgroundColor: (theme) => theme.palette.grey[300],
|
|
282
|
+
borderRadius: 3,
|
|
283
|
+
opacity: isOpeningGesture ? 0 : 1,
|
|
284
|
+
transition: 'opacity 120ms ease',
|
|
285
|
+
}}
|
|
286
|
+
/>
|
|
287
|
+
</Box>
|
|
288
|
+
)}
|
|
172
289
|
<Box
|
|
173
290
|
role="button"
|
|
174
291
|
aria-label={t('form.labels.payment_overview_open')}
|
|
175
292
|
onClick={toggleDrawer}
|
|
293
|
+
onTouchStart={(event: React.TouchEvent<HTMLDivElement>) => {
|
|
294
|
+
handleGestureStart(event.touches[0]?.clientY ?? null);
|
|
295
|
+
}}
|
|
296
|
+
onMouseDown={() => {
|
|
297
|
+
handleGestureStart(null);
|
|
298
|
+
}}
|
|
299
|
+
onTouchMove={(event: React.TouchEvent<HTMLDivElement>) => {
|
|
300
|
+
handleGestureMove(event.touches[0]?.clientY);
|
|
301
|
+
}}
|
|
302
|
+
onTouchCancel={handleGestureCancel}
|
|
303
|
+
onTouchEnd={() => {
|
|
304
|
+
handleGestureEnd({ shouldResetTouchStart: true });
|
|
305
|
+
}}
|
|
306
|
+
onMouseUp={() => {
|
|
307
|
+
handleGestureEnd();
|
|
308
|
+
}}
|
|
176
309
|
sx={{
|
|
177
310
|
width: '100%',
|
|
178
311
|
px: 2,
|
|
179
312
|
pb: 2,
|
|
180
|
-
pointerEvents:
|
|
313
|
+
pointerEvents: isDrawerOpened || isDrawerHidden ? 'none' : 'auto',
|
|
181
314
|
cursor: 'pointer',
|
|
182
315
|
}}
|
|
183
316
|
>
|
|
@@ -212,75 +345,51 @@ const PaymentOverviewDrawer: React.FC<Props> = ({ event, totalPrice, onOpenChang
|
|
|
212
345
|
{/* Drawer Content */}
|
|
213
346
|
<SwipeableDrawer
|
|
214
347
|
anchor="bottom"
|
|
215
|
-
open={
|
|
216
|
-
onClose={closeDrawerHandler}
|
|
217
|
-
onOpen={openDrawerHandler}
|
|
348
|
+
open={isDrawerOpened}
|
|
349
|
+
onClose={() => closeDrawerHandler()}
|
|
350
|
+
onOpen={() => openDrawerHandler()}
|
|
218
351
|
swipeAreaWidth={swipeAreaWidth}
|
|
219
352
|
allowSwipeInChildren
|
|
220
|
-
disableSwipeToOpen={
|
|
353
|
+
disableSwipeToOpen={isDrawerHidden}
|
|
221
354
|
keepMounted
|
|
222
355
|
SwipeAreaProps={{
|
|
223
356
|
onTouchStart: (event: React.TouchEvent<HTMLDivElement>) => {
|
|
224
|
-
|
|
225
|
-
setIsOpeningGesture(true);
|
|
226
|
-
setHasPulledPastClosedTabHeight(false);
|
|
357
|
+
handleGestureStart(event.touches[0]?.clientY ?? null);
|
|
227
358
|
},
|
|
228
359
|
onMouseDown: () => {
|
|
229
|
-
|
|
230
|
-
setHasPulledPastClosedTabHeight(false);
|
|
360
|
+
handleGestureStart(null);
|
|
231
361
|
},
|
|
232
362
|
onTouchMove: (event: React.TouchEvent<HTMLDivElement>) => {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const startY = openingTouchStartYRef.current;
|
|
236
|
-
const currentY = event.touches[0]?.clientY;
|
|
237
|
-
if (startY === null || typeof currentY !== 'number') return;
|
|
238
|
-
|
|
239
|
-
const pullDistance = startY - currentY;
|
|
240
|
-
const closedTabHeight = closedTabRef.current?.offsetHeight ?? 0;
|
|
241
|
-
setHasPulledPastClosedTabHeight(closedTabHeight > 0 && pullDistance >= closedTabHeight);
|
|
242
|
-
},
|
|
243
|
-
onTouchCancel: () => {
|
|
244
|
-
openingTouchStartYRef.current = null;
|
|
245
|
-
setIsOpeningGesture(false);
|
|
246
|
-
setHasPulledPastClosedTabHeight(false);
|
|
363
|
+
handleGestureMove(event.touches[0]?.clientY);
|
|
247
364
|
},
|
|
365
|
+
onTouchCancel: handleGestureCancel,
|
|
248
366
|
onTouchEnd: () => {
|
|
249
|
-
|
|
250
|
-
if (!openDrawer) {
|
|
251
|
-
setIsOpeningGesture(false);
|
|
252
|
-
if (resetPulledPastTimeoutRef.current !== null) {
|
|
253
|
-
window.clearTimeout(resetPulledPastTimeoutRef.current);
|
|
254
|
-
}
|
|
255
|
-
resetPulledPastTimeoutRef.current = window.setTimeout(() => {
|
|
256
|
-
if (!openDrawerRef.current) {
|
|
257
|
-
setHasPulledPastClosedTabHeight(false);
|
|
258
|
-
}
|
|
259
|
-
}, 180);
|
|
260
|
-
}
|
|
367
|
+
handleGestureEnd({ shouldResetTouchStart: true });
|
|
261
368
|
},
|
|
262
369
|
onMouseUp: () => {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
370
|
+
handleGestureEnd();
|
|
371
|
+
},
|
|
372
|
+
}}
|
|
373
|
+
sx={{
|
|
374
|
+
display: { md: 'none' },
|
|
375
|
+
pointerEvents: isDrawerOpened ? 'none' : 'auto',
|
|
376
|
+
'& .MuiDrawer-paper': {
|
|
377
|
+
pointerEvents: 'auto',
|
|
378
|
+
},
|
|
379
|
+
'& .MuiSwipeArea-root': {
|
|
380
|
+
pointerEvents: 'auto',
|
|
274
381
|
},
|
|
275
382
|
}}
|
|
276
|
-
sx={{ display: { md: 'none' } }}
|
|
277
383
|
ModalProps={{
|
|
278
384
|
disableScrollLock: true,
|
|
279
385
|
keepMounted: false,
|
|
386
|
+
disableAutoFocus: true,
|
|
387
|
+
disableRestoreFocus: true,
|
|
388
|
+
disableEnforceFocus: true,
|
|
280
389
|
BackdropProps: {
|
|
281
|
-
sx: {
|
|
282
|
-
|
|
283
|
-
|
|
390
|
+
sx: {
|
|
391
|
+
background: 'none',
|
|
392
|
+
pointerEvents: 'none',
|
|
284
393
|
},
|
|
285
394
|
},
|
|
286
395
|
}}
|
|
@@ -291,10 +400,11 @@ const PaymentOverviewDrawer: React.FC<Props> = ({ event, totalPrice, onOpenChang
|
|
|
291
400
|
boxShadow: '0 -8px 10px 2px rgba(0,0,0,0.2)',
|
|
292
401
|
overflow: 'hidden',
|
|
293
402
|
mb:
|
|
294
|
-
|
|
403
|
+
isDrawerOpened ||
|
|
404
|
+
(!isDrawerOpened && (isOpeningGesture || hasPulledPastClosedTabHeight))
|
|
295
405
|
? `${closedTabHeight}px`
|
|
296
406
|
: 0,
|
|
297
|
-
transition: '
|
|
407
|
+
transition: 'none',
|
|
298
408
|
},
|
|
299
409
|
}}
|
|
300
410
|
>
|
|
@@ -326,7 +436,7 @@ const PaymentOverviewDrawer: React.FC<Props> = ({ event, totalPrice, onOpenChang
|
|
|
326
436
|
overflow: 'auto',
|
|
327
437
|
}}
|
|
328
438
|
>
|
|
329
|
-
<PaymentOverviewBox event={event} />
|
|
439
|
+
<PaymentOverviewBox event={event} hideBuyButton />
|
|
330
440
|
</Box>
|
|
331
441
|
</SwipeableDrawer>
|
|
332
442
|
</Box>
|
package/src/form/TicketForm.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import PaymentSuccess from '@form/PaymentSuccess';
|
|
|
3
3
|
import FormProvider, { RHFCheckbox } from '@components/hook-form';
|
|
4
4
|
import {
|
|
5
5
|
Box,
|
|
6
|
+
Divider,
|
|
6
7
|
Grid,
|
|
7
8
|
Link,
|
|
8
9
|
LinkProps,
|
|
@@ -52,7 +53,6 @@ import useErrors from '@hooks/useErrors';
|
|
|
52
53
|
import { EventType } from '@utils/data/event';
|
|
53
54
|
import TimeslotSelection from '@form/TimeslotSelection';
|
|
54
55
|
import useGlobal from '@hooks/useGlobal';
|
|
55
|
-
import useConsentScrollOnDrawerOpen from '@hooks/useConsentScrollOnDrawerOpen';
|
|
56
56
|
import useScrollToFirstError from '@hooks/useScrollToFirstError';
|
|
57
57
|
import { Trans } from '@components/Trans';
|
|
58
58
|
import { EVENTLOOK_ORDER_FORM_ID, EVENTLOOK_ORDER_FORM_CONTAINER_ID } from '@utils/data/global.ts';
|
|
@@ -115,11 +115,6 @@ const TicketForm: React.FC<Props> = ({
|
|
|
115
115
|
const hasFiredPaymentMethod = useRef(false);
|
|
116
116
|
const termsAndConditionsRef = useRef<HTMLDivElement | null>(null);
|
|
117
117
|
|
|
118
|
-
useConsentScrollOnDrawerOpen({
|
|
119
|
-
isDrawerOpen: isPaymentOverviewDrawerOpen,
|
|
120
|
-
consentRef: termsAndConditionsRef,
|
|
121
|
-
});
|
|
122
|
-
|
|
123
118
|
const item: IEcommerce = {
|
|
124
119
|
currency: event.currency,
|
|
125
120
|
items: [
|
|
@@ -693,14 +688,18 @@ const TicketForm: React.FC<Props> = ({
|
|
|
693
688
|
/>
|
|
694
689
|
</Stack>
|
|
695
690
|
</Grid>
|
|
696
|
-
<Grid item xs={12}
|
|
697
|
-
<
|
|
691
|
+
<Grid item xs={12} sx={{ display: { xs: 'block', md: 'none' } }}>
|
|
692
|
+
<Divider sx={{ borderStyle: 'dashed' }} />
|
|
693
|
+
</Grid>
|
|
694
|
+
<Grid item xs={12} md={4} mt={{ xs: 0, md: 0 }}>
|
|
695
|
+
<PaymentOverviewBox event={event} withoutPadding />
|
|
698
696
|
</Grid>
|
|
699
697
|
</Grid>
|
|
700
698
|
|
|
701
699
|
<PaymentOverviewDrawer
|
|
702
700
|
event={event}
|
|
703
701
|
totalPrice={values.total}
|
|
702
|
+
termsAndConditionsRef={termsAndConditionsRef}
|
|
704
703
|
onOpenChange={setIsPaymentOverviewDrawerOpen}
|
|
705
704
|
/>
|
|
706
705
|
|
|
@@ -21,7 +21,7 @@ const MerchandiseSlider: React.FC<Props> = ({ eventProducts, eventId, sx }) => {
|
|
|
21
21
|
sx={{
|
|
22
22
|
overflow: 'hidden',
|
|
23
23
|
padding: theme.spacing(8, 3),
|
|
24
|
-
margin: theme.spacing(-8, -
|
|
24
|
+
margin: theme.spacing(-8, -2.5),
|
|
25
25
|
}}
|
|
26
26
|
>
|
|
27
27
|
<Box ref={emblaRef} className="embla" sx={{ overflow: 'visible' }}>
|
|
@@ -294,6 +294,7 @@ const ProductVariantsDialog: React.FC<Props> = ({
|
|
|
294
294
|
<Button
|
|
295
295
|
variant="outlined"
|
|
296
296
|
onClick={() => handleRemoveQuantity(variant)}
|
|
297
|
+
aria-label={t('components.product_variant_dialog.decrease_quantity')}
|
|
297
298
|
disabled={draftQty < 1}
|
|
298
299
|
sx={{
|
|
299
300
|
minWidth: 0,
|
|
@@ -328,6 +329,7 @@ const ProductVariantsDialog: React.FC<Props> = ({
|
|
|
328
329
|
<Button
|
|
329
330
|
variant="contained"
|
|
330
331
|
onClick={() => handleAddQuantity(variant)}
|
|
332
|
+
aria-label={t('components.product_variant_dialog.increase_quantity')}
|
|
331
333
|
disabled={draftQty >= 10 || draftQty >= maxAvailable}
|
|
332
334
|
sx={{ minWidth: 0, height: 36, borderRadius: 1, fontWeight: 700 }}
|
|
333
335
|
>
|
package/src/form/style.ts
CHANGED
|
@@ -5,6 +5,12 @@ export const OverviewCard = styled(Card)<{ stickyHeaderTop: number }>(
|
|
|
5
5
|
({ theme, stickyHeaderTop }) => ({
|
|
6
6
|
position: 'sticky',
|
|
7
7
|
top: stickyHeaderTop,
|
|
8
|
+
borderRadius: theme.spacing(2),
|
|
9
|
+
|
|
10
|
+
[theme.breakpoints.down('sm')]: {
|
|
11
|
+
borderRadius: theme.spacing(1),
|
|
12
|
+
boxShadow: 'none',
|
|
13
|
+
},
|
|
8
14
|
})
|
|
9
15
|
);
|
|
10
16
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Box, Button, IconButton, Stack } from '@mui/material';
|
|
3
3
|
import { Iconify } from '@components/iconify';
|
|
4
|
+
import useGlobal from '@hooks/useGlobal';
|
|
4
5
|
|
|
5
6
|
interface TicketQuantityControlProps {
|
|
6
7
|
quantity: number;
|
|
@@ -23,11 +24,14 @@ const TicketQuantityControl: React.FC<TicketQuantityControlProps> = ({
|
|
|
23
24
|
onIncrement,
|
|
24
25
|
onAddFirst,
|
|
25
26
|
}) => {
|
|
27
|
+
const { t } = useGlobal();
|
|
28
|
+
|
|
26
29
|
if (quantity > 0) {
|
|
27
30
|
return (
|
|
28
31
|
<Stack direction="row" spacing={{ xs: 0.5, md: 1 }} alignItems="center">
|
|
29
32
|
<IconButton
|
|
30
33
|
onClick={onDecrement}
|
|
34
|
+
aria-label={t('form.labels.ticket_quantity_decrease')}
|
|
31
35
|
disabled={isDisabled || quantity <= 0}
|
|
32
36
|
sx={{
|
|
33
37
|
width: { xs: 36, md: 40 },
|
|
@@ -56,6 +60,7 @@ const TicketQuantityControl: React.FC<TicketQuantityControlProps> = ({
|
|
|
56
60
|
</Box>
|
|
57
61
|
<IconButton
|
|
58
62
|
onClick={onIncrement}
|
|
63
|
+
aria-label={t('form.labels.ticket_quantity_increase')}
|
|
59
64
|
disabled={isDisabled || !canAddMore}
|
|
60
65
|
sx={{
|
|
61
66
|
width: { xs: 36, md: 40 },
|
|
@@ -76,6 +81,7 @@ const TicketQuantityControl: React.FC<TicketQuantityControlProps> = ({
|
|
|
76
81
|
<Button
|
|
77
82
|
variant="contained"
|
|
78
83
|
onClick={onAddFirst}
|
|
84
|
+
aria-label={addLabel}
|
|
79
85
|
disabled={isDisabled || !canAddFirst}
|
|
80
86
|
sx={{
|
|
81
87
|
height: { xs: 36, md: 40 },
|
|
@@ -53,22 +53,23 @@ const getFirstError = <T extends FieldValues>(
|
|
|
53
53
|
return null;
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
-
const escapeSelectorValue = (value: string) => {
|
|
56
|
+
const escapeSelectorValue = (value: string): string => {
|
|
57
57
|
if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {
|
|
58
58
|
return CSS.escape(value);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
// Fallback: escape all CSS special characters manually
|
|
62
|
+
return value.replace(/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, '\\$1');
|
|
62
63
|
};
|
|
63
64
|
|
|
64
65
|
type ScrollToFirstErrorMethods<T extends FieldValues> = Pick<
|
|
65
|
-
UseFormReturn<T,
|
|
66
|
+
UseFormReturn<T, object, undefined>,
|
|
66
67
|
'setFocus'
|
|
67
68
|
>;
|
|
68
69
|
|
|
69
|
-
export default function useScrollToFirstError<T extends FieldValues>(
|
|
70
|
-
|
|
71
|
-
) {
|
|
70
|
+
export default function useScrollToFirstError<T extends FieldValues>({
|
|
71
|
+
setFocus,
|
|
72
|
+
}: ScrollToFirstErrorMethods<T>) {
|
|
72
73
|
return useCallback(
|
|
73
74
|
(errors: FieldErrors<T>) => {
|
|
74
75
|
const firstError = getFirstError(errors);
|
|
@@ -76,19 +77,23 @@ export default function useScrollToFirstError<T extends FieldValues>(
|
|
|
76
77
|
return;
|
|
77
78
|
}
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
// Re-query the DOM instead of relying on potentially stale refs
|
|
81
|
+
const fieldName = String(firstError.name);
|
|
81
82
|
const scrollTarget =
|
|
82
|
-
|
|
83
|
-
document.
|
|
84
|
-
`[name="${escapeSelectorValue(String(firstError.name))}"]`
|
|
85
|
-
);
|
|
83
|
+
document.querySelector<HTMLElement>(`[name="${escapeSelectorValue(fieldName)}"]`) ||
|
|
84
|
+
document.getElementById(fieldName);
|
|
86
85
|
|
|
87
|
-
scrollTarget
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
if (scrollTarget) {
|
|
87
|
+
scrollTarget.scrollIntoView({
|
|
88
|
+
behavior: 'smooth',
|
|
89
|
+
block: 'center',
|
|
90
|
+
});
|
|
91
|
+
scrollTarget.focus({ preventScroll: true });
|
|
92
|
+
} else {
|
|
93
|
+
// Fallback to RHF's setFocus if no DOM element found
|
|
94
|
+
setFocus(firstError.name);
|
|
95
|
+
}
|
|
91
96
|
},
|
|
92
|
-
[
|
|
97
|
+
[setFocus]
|
|
93
98
|
);
|
|
94
99
|
}
|