@applicaster/zapp-react-native-utils
Version:
Applicaster Zapp React Native utilities package
116 lines (94 loc) • 3.05 kB
text/typescript
import * as React from "react";
import * as R from "ramda";
import { focusManager } from "./FocusManager";
import { NON_FOCUSABLE_COMPONENTS } from "./const";
import { useFocusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable/index.android";
import { toBooleanWithDefaultTrue } from "../booleanUtils";
import { isNilOrEmpty } from "../reactUtils/helpers";
import { isAndroidTVPlatform } from "../reactUtils";
import { noop } from "../functionUtils";
export function useFocusManager() {
return focusManager;
}
export function useInitialFocus(
focused: boolean,
initialRef?: FocusManager.TouchableReactRef | string,
options: {
refsList?: Array<FocusManager.TouchableReactRef> | Array<string>;
withStateMemory?: boolean;
initialFocusDirection?: FocusManager.Android.FocusNavigationDirections;
initialScrollIndex?: number;
} = {}
): ((index: number) => void) | (() => void) {
const { withStateMemory, refsList } = options;
const currentlyFocusedIndex = React.useRef<number>(
options?.initialScrollIndex || 0
);
const setCurrentlyFocusedIndex = React.useCallback(
(index: number) => (currentlyFocusedIndex.current = index),
[]
);
const focusManager = useFocusManager();
const nextFocus =
(withStateMemory && refsList?.[currentlyFocusedIndex.current]) ||
initialRef;
React.useEffect(() => {
if (focused && nextFocus) {
focusManager.setFocus(nextFocus, {
initialFocusDirection: options?.initialFocusDirection,
});
}
}, [focused, initialRef]);
if (withStateMemory) {
return setCurrentlyFocusedIndex;
}
return noop;
}
export function useFocusRefs() {
const { current } = React.useRef([]);
return current;
}
export function useFocusEffect(onFocus, focusWatcher) {
const [focused] = focusWatcher;
React.useLayoutEffect(() => {
let onBlur = () => null;
if (focused) {
onBlur = onFocus?.();
} else {
onBlur?.();
}
}, focusWatcher);
}
export const isFocusable = (component: ZappEntry | ZappUIComponent | any) =>
R.tryCatch(
R.compose(
R.not,
R.includes(R.__, NON_FOCUSABLE_COMPONENTS),
R.prop("component_type")
),
R.T
)(component);
type Props = {
component: ZappUIComponent;
zappPipesData?: ZappPipesData;
} & Record<string, any>;
export const useIsFocusable = (props: Props): void => {
const { component, zappPipesData } = props;
const { setIsFocusable } = useFocusable();
const componentCellsFocusable = toBooleanWithDefaultTrue(
component?.rules?.component_cells_focusable
);
const hideIfDataEmpty = toBooleanWithDefaultTrue(
component.rules.hide_component_if_data_is_empty
);
const hasNoData =
isNilOrEmpty(zappPipesData?.data) ||
isNilOrEmpty(zappPipesData?.data?.entry);
React.useEffect(() => {
if (isAndroidTVPlatform()) {
setIsFocusable(
componentCellsFocusable && (!hasNoData || !hideIfDataEmpty)
);
}
}, [setIsFocusable, componentCellsFocusable, hasNoData]);
};