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