zkit-ui 2.0.20 → 2.0.22
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/ui/SliderCaptcha/index.d.ts.map +1 -1
- package/dist/ui/SliderCaptcha/index.js +68 -3
- package/dist/ui/WheelColumn/AndroidVirtualizedWheel.d.ts.map +1 -1
- package/dist/ui/WheelColumn/AndroidVirtualizedWheel.js +1 -1
- package/package.json +1 -1
- package/src/ui/SliderCaptcha/README.md +1 -0
- package/src/ui/SliderCaptcha/index.tsx +97 -3
- package/src/ui/WheelColumn/AndroidVirtualizedWheel.tsx +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/SliderCaptcha/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,SAAS,EAGT,SAAS,EAEV,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/SliderCaptcha/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,SAAS,EAGT,SAAS,EAEV,MAAM,cAAc,CAAC;AAiCtB,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AACzF,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AACnE,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAExD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,0BAA0B,CACpC,UAAU,SAAS,sBAAsB,GAAG,sBAAsB,IAChE;IACF,SAAS,EAAE,UAAU,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,yBAAyB,GACjC,OAAO,GACP,IAAI,GACJ;IACE,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEN,MAAM,MAAM,sBAAsB,CAChC,UAAU,SAAS,sBAAsB,GAAG,sBAAsB,IAChE;IACF,KAAK,EAAE,uBAAuB,CAAC;IAC/B,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,SAAS,CAAC,EAAE,UAAU,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,kBAAkB,CAC5B,UAAU,SAAS,sBAAsB,GAAG,sBAAsB,IAChE;IACF,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IACrC,aAAa,EACT,CAAC,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,uBAAuB,CAAA;KAAE,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC,GACvE,CAAC,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,uBAAuB,CAAA;KAAE,KAAK,UAAU,CAAC,CAAC;IACnE,eAAe,EACX,CAAC,CACC,OAAO,EAAE,0BAA0B,CAAC,UAAU,CAAC,KAC5C,OAAO,CAAC,yBAAyB,CAAC,CAAC,GACxC,CAAC,CAAC,OAAO,EAAE,0BAA0B,CAAC,UAAU,CAAC,KAAK,yBAAyB,CAAC,CAAC;IACrF,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,0BAA0B,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IACvE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,sBAAsB,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IAC7E,KAAK,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACpC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAClC,CAAC;AA2NF,wBAAgB,aAAa,CAAC,UAAU,SAAS,sBAAsB,GAAG,sBAAsB,EAAE,EAChG,OAAO,EACP,OAAO,EACP,gBAAuB,EACvB,aAAa,EACb,eAAe,EACf,UAAU,EACV,OAAO,EACP,KAAK,EACL,sBAA6B,EAC7B,YAAmB,EACnB,uBAAkD,EAClD,qBAA8C,EAC9C,YAAqC,EACrC,SAAS,GACV,EAAE,kBAAkB,CAAC,UAAU,CAAC,kDAokBhC"}
|
|
@@ -55,6 +55,8 @@ const CARD_HORIZONTAL_PADDING = (0, zkit_tools_1.wp)(24);
|
|
|
55
55
|
const CANVAS_BORDER_WIDTH = (0, zkit_tools_1.wp)(1);
|
|
56
56
|
const CAPTCHA_LAYER_Z_INDEX = 4000;
|
|
57
57
|
const SLIDER_THUMB_SIZE = (0, zkit_tools_1.wp)(44);
|
|
58
|
+
const WEB_SLIDER_EDGE_GESTURE_INSET = react_native_1.Platform.OS === 'web' ? (0, zkit_tools_1.wp)(16) : 0;
|
|
59
|
+
const SLIDER_CANCEL_DURATION = 160;
|
|
58
60
|
const CAPTCHA_ENTER_DURATION = 280;
|
|
59
61
|
const CAPTCHA_EXIT_DURATION = 220;
|
|
60
62
|
const CAPTCHA_CARD_OFFSET_Y = (0, zkit_tools_1.wp)(18);
|
|
@@ -89,6 +91,51 @@ function getSafePositiveNumber(value, fallback) {
|
|
|
89
91
|
}
|
|
90
92
|
return value;
|
|
91
93
|
}
|
|
94
|
+
let webHorizontalOverscrollLockCount = 0;
|
|
95
|
+
let webHorizontalOverscrollSnapshots = [];
|
|
96
|
+
function acquireWebHorizontalOverscrollLock() {
|
|
97
|
+
if (react_native_1.Platform.OS !== 'web')
|
|
98
|
+
return undefined;
|
|
99
|
+
const webDocument = globalThis.document;
|
|
100
|
+
const targets = [webDocument === null || webDocument === void 0 ? void 0 : webDocument.documentElement, webDocument === null || webDocument === void 0 ? void 0 : webDocument.body].filter((target) => Boolean(target));
|
|
101
|
+
if (targets.length === 0)
|
|
102
|
+
return undefined;
|
|
103
|
+
if (webHorizontalOverscrollLockCount === 0) {
|
|
104
|
+
webHorizontalOverscrollSnapshots = targets.map((target) => ({
|
|
105
|
+
previousValue: target.style.overscrollBehaviorX,
|
|
106
|
+
target,
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
webHorizontalOverscrollLockCount += 1;
|
|
110
|
+
targets.forEach((target) => {
|
|
111
|
+
target.style.overscrollBehaviorX = 'none';
|
|
112
|
+
});
|
|
113
|
+
let released = false;
|
|
114
|
+
return () => {
|
|
115
|
+
if (released)
|
|
116
|
+
return;
|
|
117
|
+
released = true;
|
|
118
|
+
webHorizontalOverscrollLockCount = Math.max(webHorizontalOverscrollLockCount - 1, 0);
|
|
119
|
+
if (webHorizontalOverscrollLockCount > 0)
|
|
120
|
+
return;
|
|
121
|
+
webHorizontalOverscrollSnapshots.forEach(({ previousValue, target }) => {
|
|
122
|
+
if (target.style.overscrollBehaviorX === 'none') {
|
|
123
|
+
target.style.overscrollBehaviorX = previousValue;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
webHorizontalOverscrollSnapshots = [];
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function resolveWebSliderInteractionStyle(canSlide) {
|
|
130
|
+
if (react_native_1.Platform.OS !== 'web')
|
|
131
|
+
return undefined;
|
|
132
|
+
return {
|
|
133
|
+
cursor: canSlide ? 'grab' : 'default',
|
|
134
|
+
overscrollBehaviorX: 'none',
|
|
135
|
+
touchAction: 'none',
|
|
136
|
+
userSelect: 'none',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
92
139
|
const styles = react_native_1.StyleSheet.create({
|
|
93
140
|
modalRoot: {
|
|
94
141
|
...react_native_1.StyleSheet.absoluteFillObject,
|
|
@@ -192,7 +239,7 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
192
239
|
},
|
|
193
240
|
sliderThumb: {
|
|
194
241
|
position: 'absolute',
|
|
195
|
-
left:
|
|
242
|
+
left: WEB_SLIDER_EDGE_GESTURE_INSET,
|
|
196
243
|
width: SLIDER_THUMB_SIZE,
|
|
197
244
|
height: SLIDER_THUMB_SIZE,
|
|
198
245
|
borderRadius: SLIDER_THUMB_SIZE / 2,
|
|
@@ -265,7 +312,7 @@ function SliderCaptcha({ visible, onClose, initialChallenge = null, loadChalleng
|
|
|
265
312
|
const blockDisplayHeight = blockHeight * scale;
|
|
266
313
|
const blockTop = Math.max(0, ((_a = challenge === null || challenge === void 0 ? void 0 : challenge.blockY) !== null && _a !== void 0 ? _a : 0) * scale);
|
|
267
314
|
const maxBlockOffset = Math.max(canvasContentWidth - blockDisplayWidth, 0);
|
|
268
|
-
const maxThumbOffset = Math.max(canvasOuterWidth - SLIDER_THUMB_SIZE, 0);
|
|
315
|
+
const maxThumbOffset = Math.max(canvasOuterWidth - SLIDER_THUMB_SIZE - WEB_SLIDER_EDGE_GESTURE_INSET * 2, 0);
|
|
269
316
|
const blockTranslateRatio = maxThumbOffset > 0 ? maxBlockOffset / maxThumbOffset : 0;
|
|
270
317
|
const isBusy = status === 'loading' || status === 'verifying';
|
|
271
318
|
const canSlide = visible && Boolean(challenge) && !isBusy;
|
|
@@ -292,6 +339,7 @@ function SliderCaptcha({ visible, onClose, initialChallenge = null, loadChalleng
|
|
|
292
339
|
opacity: 0.15,
|
|
293
340
|
radius: (0, zkit_tools_1.wp)(4),
|
|
294
341
|
}), [theme.colors.onSurface]);
|
|
342
|
+
const webSliderInteractionStyle = React.useMemo(() => resolveWebSliderInteractionStyle(canSlide), [canSlide]);
|
|
295
343
|
const requestChallenge = React.useCallback(async (reason) => {
|
|
296
344
|
if (!visibleRef.current)
|
|
297
345
|
return;
|
|
@@ -476,6 +524,11 @@ function SliderCaptcha({ visible, onClose, initialChallenge = null, loadChalleng
|
|
|
476
524
|
});
|
|
477
525
|
return () => subscription.remove();
|
|
478
526
|
}, [onClose, visible]);
|
|
527
|
+
React.useEffect(() => {
|
|
528
|
+
if (!visible || react_native_1.Platform.OS !== 'web')
|
|
529
|
+
return;
|
|
530
|
+
return acquireWebHorizontalOverscrollLock();
|
|
531
|
+
}, [visible]);
|
|
479
532
|
const handleCanvasLayout = React.useCallback((event) => {
|
|
480
533
|
const nextWidth = event.nativeEvent.layout.width;
|
|
481
534
|
if (!Number.isFinite(nextWidth) || nextWidth <= 0)
|
|
@@ -521,6 +574,14 @@ function SliderCaptcha({ visible, onClose, initialChallenge = null, loadChalleng
|
|
|
521
574
|
const blockAnimatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => ({
|
|
522
575
|
transform: [{ translateX: thumbX.value * blockTranslateRatio }],
|
|
523
576
|
}));
|
|
577
|
+
const cancelCurrentDrag = React.useCallback(() => {
|
|
578
|
+
const startPosition = clamp(thumbStartRef.current, 0, maxThumbOffset);
|
|
579
|
+
thumbXRef.current = startPosition;
|
|
580
|
+
thumbX.value = (0, react_native_reanimated_1.withTiming)(startPosition, {
|
|
581
|
+
duration: SLIDER_CANCEL_DURATION,
|
|
582
|
+
easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.cubic),
|
|
583
|
+
});
|
|
584
|
+
}, [maxThumbOffset, thumbX]);
|
|
524
585
|
const panResponder = React.useMemo(() => react_native_1.PanResponder.create({
|
|
525
586
|
onStartShouldSetPanResponder: () => canSlide,
|
|
526
587
|
onMoveShouldSetPanResponder: () => canSlide,
|
|
@@ -537,7 +598,10 @@ function SliderCaptcha({ visible, onClose, initialChallenge = null, loadChalleng
|
|
|
537
598
|
onPanResponderRelease: () => {
|
|
538
599
|
void verifyCurrentChallenge();
|
|
539
600
|
},
|
|
540
|
-
|
|
601
|
+
onPanResponderTerminate: cancelCurrentDrag,
|
|
602
|
+
onPanResponderTerminationRequest: () => false,
|
|
603
|
+
onShouldBlockNativeResponder: () => true,
|
|
604
|
+
}), [canSlide, cancelCurrentDrag, maxThumbOffset, thumbX, verifyCurrentChallenge]);
|
|
541
605
|
if (!mounted)
|
|
542
606
|
return null;
|
|
543
607
|
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { pointerEvents: "box-none", style: styles.modalRoot, children: [(0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { style: [styles.overlayBackdrop, backdropAnimatedStyle] }), (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { style: styles.overlayPressable, onPress: handleBackdropPress, children: (0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { style: [styles.cardContainer, cardAnimatedStyle], collapsable: false, renderToHardwareTextureAndroid: react_native_1.Platform.OS === 'android', shouldRasterizeIOS: react_native_1.Platform.OS === 'ios', needsOffscreenAlphaCompositing: react_native_1.Platform.OS === 'android', children: (0, jsx_runtime_1.jsxs)(react_native_1.Pressable, { style: [
|
|
@@ -584,6 +648,7 @@ function SliderCaptcha({ visible, onClose, initialChallenge = null, loadChalleng
|
|
|
584
648
|
], children: (0, jsx_runtime_1.jsx)(Text_1.Text, { style: styles.feedbackText, children: feedbackMessage }) })) : null] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
585
649
|
styles.sliderTrack,
|
|
586
650
|
{ backgroundColor: theme.colors.secondary, borderColor: theme.colors.border },
|
|
651
|
+
webSliderInteractionStyle,
|
|
587
652
|
], ...panResponder.panHandlers, children: (0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { style: [
|
|
588
653
|
styles.sliderThumb,
|
|
589
654
|
thumbAnimatedStyle,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AndroidVirtualizedWheel.d.ts","sourceRoot":"","sources":["../../../src/ui/WheelColumn/AndroidVirtualizedWheel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAIL,KAAK,UAAU,EAGf,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,MAAM,MAAM,6BAA6B,GAAG;IAC1C,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3D,eAAe,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,KAAK,MAAM,CAAC;CACjD,CAAC;AAEF,KAAK,4BAA4B,GAAG;IAClC,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,UAAU,CAAC;IAC1B,iBAAiB,EAAE,UAAU,CAAC;IAC9B,iBAAiB,EAAE,UAAU,CAAC;IAC9B,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,qBAAqB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7C,qBAAqB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACtE,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,CAAC;AAYF,eAAO,MAAM,uBAAuB,
|
|
1
|
+
{"version":3,"file":"AndroidVirtualizedWheel.d.ts","sourceRoot":"","sources":["../../../src/ui/WheelColumn/AndroidVirtualizedWheel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAIL,KAAK,UAAU,EAGf,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,MAAM,MAAM,6BAA6B,GAAG;IAC1C,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3D,eAAe,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,KAAK,MAAM,CAAC;CACjD,CAAC;AAEF,KAAK,4BAA4B,GAAG;IAClC,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,UAAU,CAAC;IAC1B,iBAAiB,EAAE,UAAU,CAAC;IAC9B,iBAAiB,EAAE,UAAU,CAAC;IAC9B,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,qBAAqB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7C,qBAAqB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACtE,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,CAAC;AAYF,eAAO,MAAM,uBAAuB,oHAiMnC,CAAC"}
|
|
@@ -148,7 +148,7 @@ exports.AndroidVirtualizedWheel = React.forwardRef(function AndroidVirtualizedWh
|
|
|
148
148
|
const handleLoad = React.useCallback(() => {
|
|
149
149
|
scrollToIndex(selectedIndex, false);
|
|
150
150
|
}, [scrollToIndex, selectedIndex]);
|
|
151
|
-
return ((0, jsx_runtime_1.jsx)(flash_list_1.FlashList, { ref: listRef, data: options, contentContainerStyle: contentContainerStyle, decelerationRate: "fast", drawDistance: itemHeight * (visibleItems + 2), extraData: selectedIndex, keyExtractor: optionKey, keyboardShouldPersistTaps: "handled", maintainVisibleContentPosition: DISABLED_MAINTAIN_VISIBLE_CONTENT_POSITION, nestedScrollEnabled: true, onMomentumScrollBegin: handleMomentumScrollBegin, onMomentumScrollEnd: handleMomentumScrollEnd, onLoad: handleLoad, onScroll: handleScroll, onScrollBeginDrag: handleScrollBeginDrag, onScrollEndDrag: handleScrollEndDrag, overScrollMode: "never", renderItem: renderItem, scrollEnabled: canInteract, scrollEventThrottle: 16, showsVerticalScrollIndicator: false, snapToAlignment: "start", snapToInterval: itemHeight, style: styles.list }));
|
|
151
|
+
return ((0, jsx_runtime_1.jsx)(flash_list_1.FlashList, { ref: listRef, data: options, contentContainerStyle: contentContainerStyle, decelerationRate: "fast", drawDistance: itemHeight * (visibleItems + 2), extraData: selectedIndex, initialScrollIndex: selectedIndex, keyExtractor: optionKey, keyboardShouldPersistTaps: "handled", maintainVisibleContentPosition: DISABLED_MAINTAIN_VISIBLE_CONTENT_POSITION, nestedScrollEnabled: true, onMomentumScrollBegin: handleMomentumScrollBegin, onMomentumScrollEnd: handleMomentumScrollEnd, onLoad: handleLoad, onScroll: handleScroll, onScrollBeginDrag: handleScrollBeginDrag, onScrollEndDrag: handleScrollEndDrag, overScrollMode: "never", renderItem: renderItem, scrollEnabled: canInteract, scrollEventThrottle: 16, showsVerticalScrollIndicator: false, snapToAlignment: "start", snapToInterval: itemHeight, style: styles.list }));
|
|
152
152
|
});
|
|
153
153
|
const styles = react_native_1.StyleSheet.create({
|
|
154
154
|
list: {
|
package/package.json
CHANGED
|
@@ -37,6 +37,8 @@ const CARD_HORIZONTAL_PADDING = wp(24);
|
|
|
37
37
|
const CANVAS_BORDER_WIDTH = wp(1);
|
|
38
38
|
const CAPTCHA_LAYER_Z_INDEX = 4000;
|
|
39
39
|
const SLIDER_THUMB_SIZE = wp(44);
|
|
40
|
+
const WEB_SLIDER_EDGE_GESTURE_INSET = Platform.OS === 'web' ? wp(16) : 0;
|
|
41
|
+
const SLIDER_CANCEL_DURATION = 160;
|
|
40
42
|
const CAPTCHA_ENTER_DURATION = 280;
|
|
41
43
|
const CAPTCHA_EXIT_DURATION = 220;
|
|
42
44
|
const CAPTCHA_CARD_OFFSET_Y = wp(18);
|
|
@@ -152,6 +154,73 @@ function getSafePositiveNumber(value: number | undefined, fallback: number) {
|
|
|
152
154
|
return value;
|
|
153
155
|
}
|
|
154
156
|
|
|
157
|
+
type WebOverscrollTarget = {
|
|
158
|
+
style: {
|
|
159
|
+
overscrollBehaviorX: string;
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
type WebDocument = {
|
|
164
|
+
body?: WebOverscrollTarget;
|
|
165
|
+
documentElement?: WebOverscrollTarget;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
type WebOverscrollSnapshot = {
|
|
169
|
+
previousValue: string;
|
|
170
|
+
target: WebOverscrollTarget;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
let webHorizontalOverscrollLockCount = 0;
|
|
174
|
+
let webHorizontalOverscrollSnapshots: WebOverscrollSnapshot[] = [];
|
|
175
|
+
|
|
176
|
+
function acquireWebHorizontalOverscrollLock() {
|
|
177
|
+
if (Platform.OS !== 'web') return undefined;
|
|
178
|
+
|
|
179
|
+
const webDocument = (globalThis as { document?: WebDocument }).document;
|
|
180
|
+
const targets = [webDocument?.documentElement, webDocument?.body].filter(
|
|
181
|
+
(target): target is WebOverscrollTarget => Boolean(target)
|
|
182
|
+
);
|
|
183
|
+
if (targets.length === 0) return undefined;
|
|
184
|
+
|
|
185
|
+
if (webHorizontalOverscrollLockCount === 0) {
|
|
186
|
+
webHorizontalOverscrollSnapshots = targets.map((target) => ({
|
|
187
|
+
previousValue: target.style.overscrollBehaviorX,
|
|
188
|
+
target,
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
webHorizontalOverscrollLockCount += 1;
|
|
193
|
+
targets.forEach((target) => {
|
|
194
|
+
target.style.overscrollBehaviorX = 'none';
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
let released = false;
|
|
198
|
+
return () => {
|
|
199
|
+
if (released) return;
|
|
200
|
+
released = true;
|
|
201
|
+
webHorizontalOverscrollLockCount = Math.max(webHorizontalOverscrollLockCount - 1, 0);
|
|
202
|
+
if (webHorizontalOverscrollLockCount > 0) return;
|
|
203
|
+
|
|
204
|
+
webHorizontalOverscrollSnapshots.forEach(({ previousValue, target }) => {
|
|
205
|
+
if (target.style.overscrollBehaviorX === 'none') {
|
|
206
|
+
target.style.overscrollBehaviorX = previousValue;
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
webHorizontalOverscrollSnapshots = [];
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function resolveWebSliderInteractionStyle(canSlide: boolean): StyleProp<ViewStyle> {
|
|
214
|
+
if (Platform.OS !== 'web') return undefined;
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
cursor: canSlide ? 'grab' : 'default',
|
|
218
|
+
overscrollBehaviorX: 'none',
|
|
219
|
+
touchAction: 'none',
|
|
220
|
+
userSelect: 'none',
|
|
221
|
+
} as unknown as ViewStyle;
|
|
222
|
+
}
|
|
223
|
+
|
|
155
224
|
const styles = StyleSheet.create({
|
|
156
225
|
modalRoot: {
|
|
157
226
|
...StyleSheet.absoluteFillObject,
|
|
@@ -255,7 +324,7 @@ const styles = StyleSheet.create({
|
|
|
255
324
|
},
|
|
256
325
|
sliderThumb: {
|
|
257
326
|
position: 'absolute',
|
|
258
|
-
left:
|
|
327
|
+
left: WEB_SLIDER_EDGE_GESTURE_INSET,
|
|
259
328
|
width: SLIDER_THUMB_SIZE,
|
|
260
329
|
height: SLIDER_THUMB_SIZE,
|
|
261
330
|
borderRadius: SLIDER_THUMB_SIZE / 2,
|
|
@@ -352,7 +421,10 @@ export function SliderCaptcha<TChallenge extends SliderCaptchaChallenge = Slider
|
|
|
352
421
|
const blockDisplayHeight = blockHeight * scale;
|
|
353
422
|
const blockTop = Math.max(0, (challenge?.blockY ?? 0) * scale);
|
|
354
423
|
const maxBlockOffset = Math.max(canvasContentWidth - blockDisplayWidth, 0);
|
|
355
|
-
const maxThumbOffset = Math.max(
|
|
424
|
+
const maxThumbOffset = Math.max(
|
|
425
|
+
canvasOuterWidth - SLIDER_THUMB_SIZE - WEB_SLIDER_EDGE_GESTURE_INSET * 2,
|
|
426
|
+
0
|
|
427
|
+
);
|
|
356
428
|
const blockTranslateRatio = maxThumbOffset > 0 ? maxBlockOffset / maxThumbOffset : 0;
|
|
357
429
|
const isBusy = status === 'loading' || status === 'verifying';
|
|
358
430
|
const canSlide = visible && Boolean(challenge) && !isBusy;
|
|
@@ -392,6 +464,10 @@ export function SliderCaptcha<TChallenge extends SliderCaptchaChallenge = Slider
|
|
|
392
464
|
}),
|
|
393
465
|
[theme.colors.onSurface]
|
|
394
466
|
);
|
|
467
|
+
const webSliderInteractionStyle = React.useMemo(
|
|
468
|
+
() => resolveWebSliderInteractionStyle(canSlide),
|
|
469
|
+
[canSlide]
|
|
470
|
+
);
|
|
395
471
|
|
|
396
472
|
const requestChallenge = React.useCallback(
|
|
397
473
|
async (reason: SliderCaptchaLoadReason) => {
|
|
@@ -603,6 +679,11 @@ export function SliderCaptcha<TChallenge extends SliderCaptchaChallenge = Slider
|
|
|
603
679
|
return () => subscription.remove();
|
|
604
680
|
}, [onClose, visible]);
|
|
605
681
|
|
|
682
|
+
React.useEffect(() => {
|
|
683
|
+
if (!visible || Platform.OS !== 'web') return;
|
|
684
|
+
return acquireWebHorizontalOverscrollLock();
|
|
685
|
+
}, [visible]);
|
|
686
|
+
|
|
606
687
|
const handleCanvasLayout = React.useCallback((event: LayoutChangeEvent) => {
|
|
607
688
|
const nextWidth = event.nativeEvent.layout.width;
|
|
608
689
|
if (!Number.isFinite(nextWidth) || nextWidth <= 0) return;
|
|
@@ -654,6 +735,15 @@ export function SliderCaptcha<TChallenge extends SliderCaptchaChallenge = Slider
|
|
|
654
735
|
transform: [{ translateX: thumbX.value * blockTranslateRatio }],
|
|
655
736
|
}));
|
|
656
737
|
|
|
738
|
+
const cancelCurrentDrag = React.useCallback(() => {
|
|
739
|
+
const startPosition = clamp(thumbStartRef.current, 0, maxThumbOffset);
|
|
740
|
+
thumbXRef.current = startPosition;
|
|
741
|
+
thumbX.value = withTiming(startPosition, {
|
|
742
|
+
duration: SLIDER_CANCEL_DURATION,
|
|
743
|
+
easing: Easing.out(Easing.cubic),
|
|
744
|
+
});
|
|
745
|
+
}, [maxThumbOffset, thumbX]);
|
|
746
|
+
|
|
657
747
|
const panResponder = React.useMemo(
|
|
658
748
|
() =>
|
|
659
749
|
PanResponder.create({
|
|
@@ -672,8 +762,11 @@ export function SliderCaptcha<TChallenge extends SliderCaptchaChallenge = Slider
|
|
|
672
762
|
onPanResponderRelease: () => {
|
|
673
763
|
void verifyCurrentChallenge();
|
|
674
764
|
},
|
|
765
|
+
onPanResponderTerminate: cancelCurrentDrag,
|
|
766
|
+
onPanResponderTerminationRequest: () => false,
|
|
767
|
+
onShouldBlockNativeResponder: () => true,
|
|
675
768
|
}),
|
|
676
|
-
[canSlide, maxThumbOffset, thumbX, verifyCurrentChallenge]
|
|
769
|
+
[canSlide, cancelCurrentDrag, maxThumbOffset, thumbX, verifyCurrentChallenge]
|
|
677
770
|
);
|
|
678
771
|
|
|
679
772
|
if (!mounted) return null;
|
|
@@ -809,6 +902,7 @@ export function SliderCaptcha<TChallenge extends SliderCaptchaChallenge = Slider
|
|
|
809
902
|
style={[
|
|
810
903
|
styles.sliderTrack,
|
|
811
904
|
{ backgroundColor: theme.colors.secondary, borderColor: theme.colors.border },
|
|
905
|
+
webSliderInteractionStyle,
|
|
812
906
|
]}
|
|
813
907
|
{...panResponder.panHandlers}
|
|
814
908
|
>
|
|
@@ -216,6 +216,7 @@ export const AndroidVirtualizedWheel = React.forwardRef<AndroidVirtualizedWheelH
|
|
|
216
216
|
decelerationRate="fast"
|
|
217
217
|
drawDistance={itemHeight * (visibleItems + 2)}
|
|
218
218
|
extraData={selectedIndex}
|
|
219
|
+
initialScrollIndex={selectedIndex}
|
|
219
220
|
keyExtractor={optionKey}
|
|
220
221
|
keyboardShouldPersistTaps="handled"
|
|
221
222
|
maintainVisibleContentPosition={DISABLED_MAINTAIN_VISIBLE_CONTENT_POSITION}
|