Spaces:
Sleeping
Sleeping
File size: 2,333 Bytes
6678fa1 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
import { useRef } from "react";
import { usePersistFn } from "./usePersistFn";
export interface UseCompositionReturn<
T extends HTMLInputElement | HTMLTextAreaElement,
> {
onCompositionStart: React.CompositionEventHandler<T>;
onCompositionEnd: React.CompositionEventHandler<T>;
onKeyDown: React.KeyboardEventHandler<T>;
isComposing: () => boolean;
}
export interface UseCompositionOptions<
T extends HTMLInputElement | HTMLTextAreaElement,
> {
onKeyDown?: React.KeyboardEventHandler<T>;
onCompositionStart?: React.CompositionEventHandler<T>;
onCompositionEnd?: React.CompositionEventHandler<T>;
}
type TimerResponse = ReturnType<typeof setTimeout>;
export function useComposition<
T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,
>(options: UseCompositionOptions<T> = {}): UseCompositionReturn<T> {
const {
onKeyDown: originalOnKeyDown,
onCompositionStart: originalOnCompositionStart,
onCompositionEnd: originalOnCompositionEnd,
} = options;
const c = useRef(false);
const timer = useRef<TimerResponse | null>(null);
const timer2 = useRef<TimerResponse | null>(null);
const onCompositionStart = usePersistFn((e: React.CompositionEvent<T>) => {
if (timer.current) {
clearTimeout(timer.current);
timer.current = null;
}
if (timer2.current) {
clearTimeout(timer2.current);
timer2.current = null;
}
c.current = true;
originalOnCompositionStart?.(e);
});
const onCompositionEnd = usePersistFn((e: React.CompositionEvent<T>) => {
// 使用两层 setTimeout 来处理 Safari 浏览器中 compositionEnd 先于 onKeyDown 触发的问题
timer.current = setTimeout(() => {
timer2.current = setTimeout(() => {
c.current = false;
});
});
originalOnCompositionEnd?.(e);
});
const onKeyDown = usePersistFn((e: React.KeyboardEvent<T>) => {
// 在 composition 状态下,阻止 ESC 和 Enter(非 shift+Enter)事件的冒泡
if (
c.current &&
(e.key === "Escape" || (e.key === "Enter" && !e.shiftKey))
) {
e.stopPropagation();
return;
}
originalOnKeyDown?.(e);
});
const isComposing = usePersistFn(() => {
return c.current;
});
return {
onCompositionStart,
onCompositionEnd,
onKeyDown,
isComposing,
};
}
|