create-expo-cljs-app
Version:
Create a react native application with Expo and Shadow-CLJS!
178 lines (161 loc) • 4.96 kB
JavaScript
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import { getModality } from '../modality';
import useEvent from '../useEvent';
import useLayoutEffect from '../useLayoutEffect';
/**
* Types
*/
export type HoverEventsConfig = {
contain?: ?boolean,
disabled?: ?boolean,
onHoverStart?: ?(e: any) => void,
onHoverChange?: ?(bool: boolean) => void,
onHoverUpdate?: ?(e: any) => void,
onHoverEnd?: ?(e: any) => void
};
/**
* Implementation
*/
const emptyObject = {};
const opts = { passive: true };
const lockEventType = 'react-gui:hover:lock';
const unlockEventType = 'react-gui:hover:unlock';
const supportsPointerEvent = () => !!(typeof window !== 'undefined' && window.PointerEvent != null);
function dispatchCustomEvent(
target: EventTarget,
type: string,
payload?: {
bubbles?: boolean,
cancelable?: boolean,
detail?: { [key: string]: mixed }
}
) {
const event = document.createEvent('CustomEvent');
const { bubbles = true, cancelable = true, detail } = payload || emptyObject;
event.initCustomEvent(type, bubbles, cancelable, detail);
target.dispatchEvent(event);
}
// This accounts for the non-PointerEvent fallback events.
function getPointerType(event) {
const { pointerType } = event;
return pointerType != null ? pointerType : getModality();
}
export default function useHover(targetRef: any, config: HoverEventsConfig): void {
const { contain, disabled, onHoverStart, onHoverChange, onHoverUpdate, onHoverEnd } = config;
const canUsePE = supportsPointerEvent();
const addMoveListener = useEvent(canUsePE ? 'pointermove' : 'mousemove', opts);
const addEnterListener = useEvent(canUsePE ? 'pointerenter' : 'mouseenter', opts);
const addLeaveListener = useEvent(canUsePE ? 'pointerleave' : 'mouseleave', opts);
// These custom events are used to implement the "contain" prop.
const addLockListener = useEvent(lockEventType, opts);
const addUnlockListener = useEvent(unlockEventType, opts);
useLayoutEffect(() => {
const target = targetRef.current;
if (target !== null) {
/**
* End the hover gesture
*/
const hoverEnd = function (e) {
if (onHoverEnd != null) {
onHoverEnd(e);
}
if (onHoverChange != null) {
onHoverChange(false);
}
// Remove the listeners once finished.
addMoveListener(target, null);
addLeaveListener(target, null);
};
/**
* Leave element
*/
const leaveListener = function (e) {
const target = targetRef.current;
if (target != null && getPointerType(e) !== 'touch') {
if (contain) {
dispatchCustomEvent(target, unlockEventType);
}
hoverEnd(e);
}
};
/**
* Move within element
*/
const moveListener = function (e) {
if (getPointerType(e) !== 'touch') {
if (onHoverUpdate != null) {
// Not all browsers have these properties
if (e.x == null) {
e.x = e.clientX;
}
if (e.y == null) {
e.y = e.clientY;
}
onHoverUpdate(e);
}
}
};
/**
* Start the hover gesture
*/
const hoverStart = function (e) {
if (onHoverStart != null) {
onHoverStart(e);
}
if (onHoverChange != null) {
onHoverChange(true);
}
// Set the listeners needed for the rest of the hover gesture.
if (onHoverUpdate != null) {
addMoveListener(target, !disabled ? moveListener : null);
}
addLeaveListener(target, !disabled ? leaveListener : null);
};
/**
* Enter element
*/
const enterListener = function (e) {
const target = targetRef.current;
if (target != null && getPointerType(e) !== 'touch') {
if (contain) {
dispatchCustomEvent(target, lockEventType);
}
hoverStart(e);
const lockListener = function (lockEvent) {
if (lockEvent.target !== target) {
hoverEnd(e);
}
};
const unlockListener = function (lockEvent) {
if (lockEvent.target !== target) {
hoverStart(e);
}
};
addLockListener(target, !disabled ? lockListener : null);
addUnlockListener(target, !disabled ? unlockListener : null);
}
};
addEnterListener(target, !disabled ? enterListener : null);
}
}, [
addEnterListener,
addMoveListener,
addLeaveListener,
addLockListener,
addUnlockListener,
contain,
disabled,
onHoverStart,
onHoverChange,
onHoverUpdate,
onHoverEnd,
targetRef
]);
}