UNPKG

@quidone/react-native-wheel-picker

Version:

Picker is a UI component for selecting an item from a list of options.

152 lines (151 loc) 6.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); var _reactUsefulHooks = require("@rozhkov/react-useful-hooks"); var _debounce = _interopRequireDefault(require("../debounce")); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } const withScrollStartEndEvent = Component => { const Wrapper = ({ onScrollStart: onScrollStartProp, onScrollEnd: onScrollEndProp, onScrollBeginDrag: onScrollBeginDragProp, onScrollEndDrag: onScrollEndDragProp, onMomentumScrollBegin: onMomentumScrollBeginProp, onMomentumScrollEnd: onMomentumScrollEndProp, scrollOffset, ...rest }, forwardedRef) => { const onScrollStartStable = (0, _reactUsefulHooks.useStableCallback)(onScrollStartProp); const isOnScrollStartCalledRef = (0, _react.useRef)(false); /* * `isImplicitScrollRef` marks a scroll session that was detected from * `contentOffset` updates only, without a native start callback. * * Why this exists: * Android may perform a programmatic animated scroll * (`scrollTo` / `scrollToIndex`) and emit several offset updates while * skipping the normal callback pair that we usually rely on: * `onScrollBeginDrag` / `onScrollEndDrag` and * `onMomentumScrollBegin` / `onMomentumScrollEnd`. * * Relevant React Native issues: * https://github.com/facebook/react-native/issues/11693 * https://github.com/facebook/react-native/issues/19246 * https://github.com/facebook/react-native/issues/25672 * https://github.com/facebook/react-native/issues/26661 * * In this library it shows up when one wheel triggers a synchronized * animated scroll in another wheel. Example sequence on Android: * - offset changes: 48 -> 47.6 -> 46.0 -> 43.4 -> ... -> 0 * - but no native "begin/end" events are dispatched for that movement * * Old failure mode: * - the first offset update inferred scroll start and scheduled a debounced * scroll end * - the next offset updates only cleared that debounced end * - because Android never sent a native end event, the session stayed * "started" forever * - `PickerControl` then kept one picker in `isStopped = false`, so the * aggregated DatePicker `onDateChanged` stopped firing * * We still emit `onScrollStart` for such a session, but we must finish it * differently: if the session is implicit, every offset update should * re-arm the debounced end so that the last offset update wins and the * scroll eventually ends even when Android never gives us a native end * callback. * * Related library issues: * https://github.com/quidone/react-native-wheel-picker/issues/56 * https://github.com/quidone/react-native-wheel-picker/issues/71 */ const isImplicitScrollRef = (0, _react.useRef)(false); const deactivateOnScrollStart = (0, _reactUsefulHooks.useStableCallback)(() => { isOnScrollStartCalledRef.current = false; isImplicitScrollRef.current = false; }); const maybeCallOnScrollStart = (0, _reactUsefulHooks.useStableCallback)(({ implicit }) => { const shouldActivate = !isOnScrollStartCalledRef.current; if (shouldActivate) { onScrollStartStable(); isOnScrollStartCalledRef.current = true; isImplicitScrollRef.current = implicit; return; } if (!implicit) { isImplicitScrollRef.current = false; } }); const maybeCallOnNativeScrollStart = (0, _reactUsefulHooks.useStableCallback)(() => { maybeCallOnScrollStart({ implicit: false }); }); const maybeCallOnImplicitScrollStart = (0, _reactUsefulHooks.useStableCallback)(() => { maybeCallOnScrollStart({ implicit: true }); }); const onScrollEndStable = (0, _reactUsefulHooks.useStableCallback)(() => { maybeCallOnNativeScrollStart(); onScrollEndProp?.(); deactivateOnScrollStart(); }); const onScrollEnd = (0, _react.useMemo)(() => (0, _debounce.default)(onScrollEndStable, 100), // A small delay is needed so that onScrollEnd doesn't trigger prematurely. [onScrollEndStable]); const onScrollBeginDrag = (0, _reactUsefulHooks.useStableCallback)(args => { maybeCallOnNativeScrollStart(); onScrollBeginDragProp?.(args); }); const onScrollEndDrag = (0, _reactUsefulHooks.useStableCallback)(args => { onScrollEndDragProp?.(args); onScrollEnd(); }); const onMomentumScrollBegin = (0, _reactUsefulHooks.useStableCallback)(args => { maybeCallOnNativeScrollStart(); onScrollEnd.clear(); onMomentumScrollBeginProp?.(args); }); const onMomentumScrollEnd = (0, _reactUsefulHooks.useStableCallback)(args => { onMomentumScrollEndProp?.(args); onScrollEnd(); }); (0, _react.useEffect)(() => { const sub = scrollOffset.addListener(() => { if (!isOnScrollStartCalledRef.current) { maybeCallOnImplicitScrollStart(); onScrollEnd(); return; } if (isImplicitScrollRef.current) { onScrollEnd(); } else { onScrollEnd.clear(); } }); return () => { scrollOffset.removeListener(sub); }; }, [maybeCallOnImplicitScrollStart, onScrollEnd, scrollOffset]); return /*#__PURE__*/(0, _jsxRuntime.jsx)(Component, { ...rest, ref: forwardedRef, onScrollBeginDrag: onScrollBeginDrag, onScrollEndDrag: onScrollEndDrag, onMomentumScrollBegin: onMomentumScrollBegin, onMomentumScrollEnd: onMomentumScrollEnd }); }; Wrapper.displayName = `withScrollStartEndEvent(${Component.displayName || 'Component'})`; return /*#__PURE__*/(0, _react.memo)(/*#__PURE__*/(0, _react.forwardRef)(Wrapper)); }; var _default = exports.default = withScrollStartEndEvent; //# sourceMappingURL=withScrollStartEndEvent.js.map