| | 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, |
| | }; |
| | } |
| |
|