Skip to content

Commit 53b8769

Browse files
committed
fix: composition input
1 parent a85e5b0 commit 53b8769

File tree

3 files changed

+60
-0
lines changed

3 files changed

+60
-0
lines changed

src/components/ui/input/Input.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import { forwardRef } from 'react'
22
import type { DetailedHTMLProps, InputHTMLAttributes } from 'react'
33

4+
import { useInputComposition } from '~/hooks/common/use-input-composition'
45
import { clsxm } from '~/lib/helper'
56

7+
// This composition handler is not perfect
8+
// @see https://foxact.skk.moe/use-composition-input
69
export const Input = forwardRef<
710
HTMLInputElement,
811
Omit<
912
DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>,
1013
'ref'
1114
>
1215
>(({ className, ...props }, ref) => {
16+
const inputProps = useInputComposition(props)
1317
return (
1418
<input
1519
ref={ref}
@@ -21,6 +25,7 @@ export const Input = forwardRef<
2125
className,
2226
)}
2327
{...props}
28+
{...inputProps}
2429
/>
2530
)
2631
})

src/components/ui/input/TextArea.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
} from 'react'
88

99
import { useIsMobile } from '~/atoms'
10+
import { useInputComposition } from '~/hooks/common/use-input-composition'
1011
import { clsxm } from '~/lib/helper'
1112

1213
export const TextArea = forwardRef<
@@ -30,6 +31,7 @@ export const TextArea = forwardRef<
3031
)
3132
const background = useMotionTemplate`radial-gradient(320px circle at ${mouseX}px ${mouseY}px, var(--spotlight-color) 0%, transparent 85%)`
3233
const isMobile = useIsMobile()
34+
const inputProps = useInputComposition(props)
3335
return (
3436
<div
3537
className="group relative h-full [--spotlight-color:hsl(var(--a)_/_0.05)]"
@@ -51,6 +53,7 @@ export const TextArea = forwardRef<
5153
className,
5254
)}
5355
{...rest}
56+
{...inputProps}
5457
/>
5558

5659
{children}
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { useCallback, useRef } from 'react'
2+
import type { CompositionEventHandler } from 'react'
3+
4+
export const useInputComposition = (
5+
props:
6+
| React.DetailedHTMLProps<
7+
React.InputHTMLAttributes<HTMLInputElement>,
8+
HTMLInputElement
9+
>
10+
| React.DetailedHTMLProps<
11+
React.TextareaHTMLAttributes<HTMLTextAreaElement>,
12+
HTMLTextAreaElement
13+
>,
14+
) => {
15+
const { onKeyDown, onCompositionStart, onCompositionEnd } = props
16+
17+
const isCompositionRef = useRef(false)
18+
19+
const handleCompositionStart: CompositionEventHandler<any> = useCallback(
20+
(e) => {
21+
isCompositionRef.current = true
22+
onCompositionStart?.(e)
23+
},
24+
[onCompositionStart],
25+
)
26+
27+
const handleCompositionEnd: CompositionEventHandler<any> = useCallback(
28+
(e) => {
29+
isCompositionRef.current = false
30+
onCompositionEnd?.(e)
31+
},
32+
[onCompositionEnd],
33+
)
34+
35+
const handleKeyDown: React.KeyboardEventHandler<any> = useCallback(
36+
(e) => {
37+
onKeyDown?.(e)
38+
39+
if (isCompositionRef.current) {
40+
e.stopPropagation()
41+
return
42+
}
43+
},
44+
[onKeyDown],
45+
)
46+
47+
return {
48+
onCompositionEnd: handleCompositionEnd,
49+
onCompositionStart: handleCompositionStart,
50+
onKeyDown: handleKeyDown,
51+
}
52+
}

0 commit comments

Comments
 (0)