48 lines
1.4 KiB
TypeScript
48 lines
1.4 KiB
TypeScript
|
|
import { useCallback, useEffect } from 'react';
|
||
|
|
import { useAtomValue, useSetAtom } from 'jotai';
|
||
|
|
import { lastCompositionEndAtom } from '../state/lastCompositionEnd';
|
||
|
|
|
||
|
|
interface TimeStamped {
|
||
|
|
readonly timeStamp: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useCompositionEndTracking(): void {
|
||
|
|
const setLastCompositionEnd = useSetAtom(lastCompositionEndAtom);
|
||
|
|
|
||
|
|
const recordCompositionEnd = useCallback(
|
||
|
|
(evt: TimeStamped) => {
|
||
|
|
setLastCompositionEnd(evt.timeStamp);
|
||
|
|
},
|
||
|
|
[setLastCompositionEnd]
|
||
|
|
);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
window.addEventListener('compositionend', recordCompositionEnd, { capture: true });
|
||
|
|
return () => {
|
||
|
|
window.removeEventListener('compositionend', recordCompositionEnd, { capture: true });
|
||
|
|
};
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
interface IsComposingLike {
|
||
|
|
readonly timeStamp: number;
|
||
|
|
readonly keyCode: number;
|
||
|
|
readonly nativeEvent: {
|
||
|
|
readonly isComposing?: boolean;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useComposingCheck({
|
||
|
|
compositionEndThreshold = 500,
|
||
|
|
}: { compositionEndThreshold?: number } = {}): (evt: IsComposingLike) => boolean {
|
||
|
|
const compositionEnd = useAtomValue(lastCompositionEndAtom);
|
||
|
|
return useCallback(
|
||
|
|
(evt: IsComposingLike): boolean =>
|
||
|
|
evt.nativeEvent.isComposing ||
|
||
|
|
(evt.keyCode === 229 &&
|
||
|
|
typeof compositionEnd !== 'undefined' &&
|
||
|
|
evt.timeStamp - compositionEnd < compositionEndThreshold),
|
||
|
|
[compositionEndThreshold, compositionEnd]
|
||
|
|
);
|
||
|
|
}
|