| 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>) => { |
| |
| timer.current = setTimeout(() => { |
| timer2.current = setTimeout(() => { |
| c.current = false; |
| }); |
| }); |
| originalOnCompositionEnd?.(e); |
| }); |
|
|
| const onKeyDown = usePersistFn((e: React.KeyboardEvent<T>) => { |
| |
| 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, |
| }; |
| } |
|
|