Spaces:
Running
Running
| import { useDialogComposition } from "@/components/ui/dialog"; | |
| import { useComposition } from "@/hooks/useComposition"; | |
| import { cn } from "@/lib/utils"; | |
| import * as React from "react"; | |
| function Textarea({ | |
| className, | |
| onKeyDown, | |
| onCompositionStart, | |
| onCompositionEnd, | |
| ...props | |
| }: React.ComponentProps<"textarea">) { | |
| // Get dialog composition context if available (will be no-op if not inside Dialog) | |
| const dialogComposition = useDialogComposition(); | |
| // Add composition event handlers to support input method editor (IME) for CJK languages. | |
| const { | |
| onCompositionStart: handleCompositionStart, | |
| onCompositionEnd: handleCompositionEnd, | |
| onKeyDown: handleKeyDown, | |
| } = useComposition<HTMLTextAreaElement>({ | |
| onKeyDown: (e) => { | |
| // Check if this is an Enter key that should be blocked | |
| const isComposing = (e.nativeEvent as any).isComposing || dialogComposition.justEndedComposing(); | |
| // If Enter key is pressed while composing or just after composition ended, | |
| // don't call the user's onKeyDown (this blocks the business logic) | |
| // Note: For textarea, Shift+Enter should still work for newlines | |
| if (e.key === "Enter" && !e.shiftKey && isComposing) { | |
| return; | |
| } | |
| // Otherwise, call the user's onKeyDown | |
| onKeyDown?.(e); | |
| }, | |
| onCompositionStart: e => { | |
| dialogComposition.setComposing(true); | |
| onCompositionStart?.(e); | |
| }, | |
| onCompositionEnd: e => { | |
| // Mark that composition just ended - this helps handle the Enter key that confirms input | |
| dialogComposition.markCompositionEnd(); | |
| // Delay setting composing to false to handle Safari's event order | |
| // In Safari, compositionEnd fires before the ESC keydown event | |
| setTimeout(() => { | |
| dialogComposition.setComposing(false); | |
| }, 100); | |
| onCompositionEnd?.(e); | |
| }, | |
| }); | |
| return ( | |
| <textarea | |
| data-slot="textarea" | |
| className={cn( | |
| "border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", | |
| className | |
| )} | |
| onCompositionStart={handleCompositionStart} | |
| onCompositionEnd={handleCompositionEnd} | |
| onKeyDown={handleKeyDown} | |
| {...props} | |
| /> | |
| ); | |
| } | |
| export { Textarea }; | |