UNPKG

react-native-focal

Version:

Humble kit to professionally handle the focus/blur experience for controlled components.

165 lines (156 loc) 6.18 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var React = require('react'); var reactNative = require('react-native'); var isFunction = require('lodash.isfunction'); var unset = require('lodash.unset'); var get = require('lodash.get'); var set = require('lodash.set'); var reactNativeGestureHandler = require('react-native-gesture-handler'); var uniqueId = require('lodash.uniqueid'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var isFunction__default = /*#__PURE__*/_interopDefaultLegacy(isFunction); var unset__default = /*#__PURE__*/_interopDefaultLegacy(unset); var get__default = /*#__PURE__*/_interopDefaultLegacy(get); var set__default = /*#__PURE__*/_interopDefaultLegacy(set); var uniqueId__default = /*#__PURE__*/_interopDefaultLegacy(uniqueId); /** * Ref containing the list of all the nodes within all the providers. */ const focuses = React.createRef(); /** * Method responsible for resetting the focal object ref to an empty object. */ const resetFocuses = () => { set__default['default'](focuses, ['current'], {}); }; /** * Gracefully `blur` the focused node via the `onBlur` method specified in the `Controller` props if it can be `blur`red. * @param force {boolean} flag to indicate whether to force removing the focused component logically or not. */ const blur = (force) => { const focusedComponent = getFocused(); if (!focusedComponent) return; const { node, onBlur } = focusedComponent; let removeFocus = true; if (isFunction__default['default'](onBlur)) removeFocus = onBlur(node); if (force || removeFocus) unset__default['default'](focuses, ['current', 'focused']); }; /** * Method responsible for retrieving the focused component value within the ref. */ const getFocused = () => { const focusedComponentRef = get__default['default'](focuses, ['current', 'focused'], ''); return get__default['default'](focuses, ['current', focusedComponentRef]); }; /** * Method responsible for retrieving the focused component id within the ref. */ const getFocusedId = () => { return get__default['default'](focuses, ['current', 'focused'], null); }; /** * Method responsible for retrieving the number of actual nodes in the focal object. */ const getLength = () => { return Object.keys(focuses.current || {}).length; }; /** * Method responsible for retrieving a certain node within the focal object via index if valid. */ const getByIndex = (index) => { if (index >= getLength()) return null; return Object.values(focuses.current || {})[index]; }; /** * Ref containing the list of all the nodes within all the providers. */ const handlers = React.createRef(); /** * Method responsible for subscribing the handler within the handlers ref object. */ const subscribeHandler = (id, ref) => { set__default['default'](handlers, ['current', id], ref); }; /** * Method responsible for unsubscribing the handler from the handlers ref object. */ const unsubscribeHandler = (id) => { unset__default['default'](handlers, ['current', id]); }; /** * Method responsible for resetting the focal object ref to an empty object. */ const resetHandlers = () => { set__default['default'](handlers, 'current', {}); }; /** * Method responsible for retrieving the handlers list. */ const getHandlers = () => { return Object.values(handlers.current || {}) || []; }; const Container = ({ children, onPress, ...props }) => { const onContainerPress = React.useCallback((_) => { blur(); if (isFunction__default['default'](onPress)) onPress(); }, [onPress]); return (React__default['default'].createElement(reactNativeGestureHandler.TapGestureHandler, { waitFor: Object.values(handlers.current || {}) || [], onActivated: onContainerPress }, React__default['default'].createElement(reactNative.Animated.View, { ...props }, children))); }; const _onBlur = (node) => { node?.blur?.(); return true; }; function Controller({ children, isFocusable = true, onBlur = _onBlur, onFocus, ...props }) { const childRef = React.useRef(null); const tapRef = React.useRef(null); const { current: privateId } = React.useRef(uniqueId__default['default']('ctrlr#')); const componentRefSetter = React.useCallback((node) => { set__default['default'](childRef, 'current', node); set__default['default'](focuses, ['current', privateId], { node, onBlur }); }, [onBlur]); const tapRefSetter = React.useCallback((node) => { set__default['default'](tapRef, 'current', node); subscribeHandler(privateId, tapRef); }, []); const onPress = React.useCallback((_) => { childRef?.current?.focus?.(); if (isFocusable) set__default['default'](focuses, ['current', 'focused'], privateId); else blur(); if (isFunction__default['default'](onFocus)) onFocus(); }, [isFocusable, onFocus]); React.useEffect(() => { return () => { unsubscribeHandler(privateId); }; }, []); return (React__default['default'].createElement(reactNativeGestureHandler.TapGestureHandler, { ref: tapRefSetter, onActivated: onPress }, React__default['default'].createElement(reactNative.View, { ...props }, React__default['default'].cloneElement(children, { ref: componentRefSetter })))); } exports.Container = Container; exports.Controller = Controller; exports.blur = blur; exports.focuses = focuses; exports.getByIndex = getByIndex; exports.getFocused = getFocused; exports.getFocusedId = getFocusedId; exports.getHandlers = getHandlers; exports.getLength = getLength; exports.handlers = handlers; exports.resetFocuses = resetFocuses; exports.resetHandlers = resetHandlers; exports.subscribeHandler = subscribeHandler; exports.unsubscribeHandler = unsubscribeHandler;