| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- import { View, Textarea, InputProps } from "@tarojs/components";
- import TextPolish from '@/components/TextPolish'
- import styleIndex from "./index.module.less";
- import { useState, useRef, useCallback, useMemo, forwardRef, useImperativeHandle } from "react";
- import { countCharacters, getStrByMaxLength } from "@/utils/index";
- interface Props {
- aiType: "personality" | "greeting"
- defaultAiTips: string;
- placeholder?: string;
- value: string;
- cursorSpacing?: number;
- maxlength?: number;
- autoHeight?: boolean;
- disabled?: boolean;
- confirmType?: keyof InputProps.ConfirmType;
- extra?: () => JSX.Element | JSX.Element[] | undefined;
- prefix?: () => JSX.Element | JSX.Element[] | undefined;
- style?: Record<string, string>;
- onInput?: (value: string) => void;
- onBlur?: (value: string) => void;
- bgColor?: string;
- autoFocus?: boolean;
- extraClass?: string;
- onConfirm?: (value: string) => void;
- }
- const DEFAULT_TEXTAREA_MAXLENGTH = 10000;
- const Index = ({
- value,
- aiType,
- defaultAiTips,
- bgColor,
- extraClass,
- style,
- disabled,
- confirmType,
- prefix,
- autoHeight,
- autoFocus = false,
- placeholder = "请输入...",
- onInput,
- onBlur,
- cursorSpacing,
- maxlength,
- extra,
- onConfirm,
- }: Props, ref: any) => {
- const [focus, setFocus] = useState(false);
- const [isPolishing, setIsPolishing] = useState(false);
- const inputRef = useRef<HTMLInputElement>(null); // 创建一个 ref
- // Create ref for TextPolish component
- const textPolishRef = useRef<{ handleClick: () => void }>(null);
- // Expose TextPolish ref to parent components
- useImperativeHandle(ref, () => ({
- handleClick: async () => {
- if (textPolishRef.current) {
- return await textPolishRef.current.handleClick();
- }
- }
- }));
- // Memoize character count to avoid recalculation
- const currentLength = useMemo(() => countCharacters(value), [value]);
- const handleFocus = () => {
- setFocus(true);
- };
- const handleBlur = () => {
- console.log("textarea blur");
- setFocus(false);
- if (onBlur && inputRef.current) {
- onBlur(inputRef.current.value);
- }
- };
- const handleInput = useCallback((inputValue: string) => {
- if (!onInput) return;
- const len = countCharacters(inputValue);
- if (maxlength && len > maxlength) {
- const r = getStrByMaxLength(inputValue, maxlength);
- onInput(r);
- return;
- }
- onInput(inputValue);
- }, [onInput, maxlength]);
- const onPolished = (text: string | null) => {
- if (text && onInput) {
- onInput(text);
- }
- };
- const handleTextareaInput = (e: any) => {
- handleInput(e.target.value);
- };
- const handleConfirm = (e: any) => {
- onConfirm && onConfirm(e.detail.value);
- };
- // Memoize container style
- const containerStyle = useMemo(() => {
- return bgColor ? { backgroundColor: bgColor } : {};
- }, [bgColor]);
- const polishText = value?.length > 0 ? value : defaultAiTips;
- return (
- <View
- className={`${
- focus ? styleIndex.inputContainerFocused : styleIndex.inputContainer
- } p-12`}
- style={containerStyle}
- >
- <View className="flex w-full pb-12">
- <View className={styleIndex.label}>
- {prefix && prefix()}
- </View>
- <View className="flex items-center gap-4 text-14 font-pingfangSCMedium">
- <TextPolish ref={textPolishRef} text={polishText} type={aiType} onPolished={onPolished} onStateChange={setIsPolishing} />
- </View>
- </View>
- <View className={styleIndex.textareaContainer}>
- <Textarea
- ref={inputRef}
- value={value}
- disabled={disabled || isPolishing}
- confirmType={confirmType}
- style={style}
- onInput={handleTextareaInput}
- placeholder={placeholder}
- placeholderStyle="rgba(17,17,17,.25)"
- className={`${styleIndex.textInput} ${extraClass || ''}`}
- onFocus={handleFocus}
- onBlur={handleBlur}
- autoHeight={autoHeight}
- autoFocus={autoFocus}
- cursorSpacing={cursorSpacing}
- maxlength={DEFAULT_TEXTAREA_MAXLENGTH}
- onConfirm={handleConfirm}
- />
- <View className={`${styleIndex.textareaButtons} justify-end gap-8`}>
- {extra && extra()}
- {maxlength && (
- <View className="text-gray-4">
- {currentLength}/{maxlength}
- </View>
- )}
- </View>
- <View className={`${ isPolishing ? styleIndex.skelonton : styleIndex.skelontonHide}`}>
- <View className={styleIndex.skelontonItem}></View>
- <View className={styleIndex.skelontonItemShort}></View>
- </View>
- </View>
- </View>
- );
- };
- export default forwardRef(Index);
|