@gechiui/block-editor
Version:
168 lines (146 loc) • 4.19 kB
JavaScript
/**
* External dependencies
*/
import { noop } from 'lodash';
/**
* GeChiUI dependencies
*/
import { useState, useRef, useEffect } from '@gechiui/element';
const {
clearTimeout,
setTimeout
} = window;
const DEBOUNCE_TIMEOUT = 200;
/**
* Hook that creates a showMover state, as well as debounced show/hide callbacks.
*
* @param {Object} props Component props.
* @param {Object} props.ref Element reference.
* @param {boolean} props.isFocused Whether the component has current focus.
* @param {number} [props.debounceTimeout=250] Debounce timeout in milliseconds.
* @param {Function} [props.onChange=noop] Callback function.
*/
export function useDebouncedShowMovers(_ref) {
let {
ref,
isFocused,
debounceTimeout = DEBOUNCE_TIMEOUT,
onChange = noop
} = _ref;
const [showMovers, setShowMovers] = useState(false);
const timeoutRef = useRef();
const handleOnChange = nextIsFocused => {
if (ref !== null && ref !== void 0 && ref.current) {
setShowMovers(nextIsFocused);
}
onChange(nextIsFocused);
};
const getIsHovered = () => {
return (ref === null || ref === void 0 ? void 0 : ref.current) && ref.current.matches(':hover');
};
const shouldHideMovers = () => {
const isHovered = getIsHovered();
return !isFocused && !isHovered;
};
const clearTimeoutRef = () => {
const timeout = timeoutRef.current;
if (timeout && clearTimeout) {
clearTimeout(timeout);
}
};
const debouncedShowMovers = event => {
if (event) {
event.stopPropagation();
}
clearTimeoutRef();
if (!showMovers) {
handleOnChange(true);
}
};
const debouncedHideMovers = event => {
if (event) {
event.stopPropagation();
}
clearTimeoutRef();
timeoutRef.current = setTimeout(() => {
if (shouldHideMovers()) {
handleOnChange(false);
}
}, debounceTimeout);
};
useEffect(() => () => clearTimeoutRef(), []);
return {
showMovers,
debouncedShowMovers,
debouncedHideMovers
};
}
/**
* Hook that provides a showMovers state and gesture events for DOM elements
* that interact with the showMovers state.
*
* @param {Object} props Component props.
* @param {Object} props.ref Element reference.
* @param {number} [props.debounceTimeout=250] Debounce timeout in milliseconds.
* @param {Function} [props.onChange=noop] Callback function.
*/
export function useShowMoversGestures(_ref2) {
let {
ref,
debounceTimeout = DEBOUNCE_TIMEOUT,
onChange = noop
} = _ref2;
const [isFocused, setIsFocused] = useState(false);
const {
showMovers,
debouncedShowMovers,
debouncedHideMovers
} = useDebouncedShowMovers({
ref,
debounceTimeout,
isFocused,
onChange
});
const registerRef = useRef(false);
const isFocusedWithin = () => {
return (ref === null || ref === void 0 ? void 0 : ref.current) && ref.current.contains(ref.current.ownerDocument.activeElement);
};
useEffect(() => {
const node = ref.current;
const handleOnFocus = () => {
if (isFocusedWithin()) {
setIsFocused(true);
debouncedShowMovers();
}
};
const handleOnBlur = () => {
if (!isFocusedWithin()) {
setIsFocused(false);
debouncedHideMovers();
}
};
/**
* Events are added via DOM events (vs. React synthetic events),
* as the child React components swallow mouse events.
*/
if (node && !registerRef.current) {
node.addEventListener('focus', handleOnFocus, true);
node.addEventListener('blur', handleOnBlur, true);
registerRef.current = true;
}
return () => {
if (node) {
node.removeEventListener('focus', handleOnFocus);
node.removeEventListener('blur', handleOnBlur);
}
};
}, [ref, registerRef, setIsFocused, debouncedShowMovers, debouncedHideMovers]);
return {
showMovers,
gestures: {
onMouseMove: debouncedShowMovers,
onMouseLeave: debouncedHideMovers
}
};
}
//# sourceMappingURL=utils.js.map