79 lines
2.0 KiB
JavaScript
79 lines
2.0 KiB
JavaScript
import { useEvent } from '@rc-component/util';
|
|
import useId from "@rc-component/util/es/hooks/useId";
|
|
import { useEffect, useMemo } from 'react';
|
|
let stack = [];
|
|
const IME_LOCK_DURATION = 200;
|
|
let lastCompositionEndTime = 0;
|
|
|
|
// Export for testing
|
|
export const _test = process.env.NODE_ENV === 'test' ? () => ({
|
|
stack,
|
|
reset: () => {
|
|
// Not reset stack to ensure effect will clean up correctly
|
|
lastCompositionEndTime = 0;
|
|
}
|
|
}) : null;
|
|
|
|
// Global event handlers
|
|
const onGlobalKeyDown = event => {
|
|
if (event.key === 'Escape' && !event.isComposing) {
|
|
const now = Date.now();
|
|
if (now - lastCompositionEndTime < IME_LOCK_DURATION) {
|
|
return;
|
|
}
|
|
const len = stack.length;
|
|
for (let i = len - 1; i >= 0; i -= 1) {
|
|
stack[i].onEsc({
|
|
top: i === len - 1,
|
|
event
|
|
});
|
|
}
|
|
}
|
|
};
|
|
const onGlobalCompositionEnd = () => {
|
|
lastCompositionEndTime = Date.now();
|
|
};
|
|
function attachGlobalEventListeners() {
|
|
window.addEventListener('keydown', onGlobalKeyDown);
|
|
window.addEventListener('compositionend', onGlobalCompositionEnd);
|
|
}
|
|
function detachGlobalEventListeners() {
|
|
if (stack.length === 0) {
|
|
window.removeEventListener('keydown', onGlobalKeyDown);
|
|
window.removeEventListener('compositionend', onGlobalCompositionEnd);
|
|
}
|
|
}
|
|
export default function useEscKeyDown(open, onEsc) {
|
|
const id = useId();
|
|
const onEventEsc = useEvent(onEsc);
|
|
const ensure = () => {
|
|
if (!stack.find(item => item.id === id)) {
|
|
stack.push({
|
|
id,
|
|
onEsc: onEventEsc
|
|
});
|
|
}
|
|
};
|
|
const clear = () => {
|
|
stack = stack.filter(item => item.id !== id);
|
|
};
|
|
useMemo(() => {
|
|
if (open) {
|
|
ensure();
|
|
} else if (!open) {
|
|
clear();
|
|
}
|
|
}, [open]);
|
|
useEffect(() => {
|
|
if (open) {
|
|
ensure();
|
|
// Attach global event listeners
|
|
attachGlobalEventListeners();
|
|
return () => {
|
|
clear();
|
|
// Remove global event listeners if instances is empty
|
|
detachGlobalEventListeners();
|
|
};
|
|
}
|
|
}, [open]);
|
|
} |