Skip to content

Commit

Permalink
feat: support sendButtonRender props (#129)
Browse files Browse the repository at this point in the history
  • Loading branch information
chenshuai2144 authored Mar 17, 2024
1 parent 48fae68 commit 1974a3b
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 45 deletions.
129 changes: 84 additions & 45 deletions src/ProChat/components/InputArea/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { SendOutlined } from '@ant-design/icons';
import { Button, ConfigProvider } from 'antd';
import { Button, ButtonProps, ConfigProvider } from 'antd';
import { createStyles, cx } from 'antd-style';
import { ReactNode, useContext, useRef, useState } from 'react';
import { ReactNode, useContext, useMemo, useRef, useState } from 'react';
import { Flexbox } from 'react-layout-kit';

import { useStore } from '../../store';

import { gLocaleObject } from '@/locale';
import { TextAreaProps } from 'antd/es/input';
import ActionBar from './ActionBar';
import { AutoCompleteTextArea } from './AutoCompleteTextArea';
import StopLoadingIcon from './StopLoading';
Expand Down Expand Up @@ -59,7 +60,9 @@ export type ChatInputAreaProps = {
inputRender?: (
defaultDom: ReactNode,
onMessageSend: (message: string) => void | Promise<any>,
defaultProps: TextAreaProps,
) => ReactNode;
sendButtonRender?: (defaultDom: ReactNode, defaultProps: ButtonProps) => ReactNode;
inputAreaRender?: (
defaultDom: ReactNode,
onMessageSend: (message: string) => void | Promise<any>,
Expand All @@ -68,7 +71,7 @@ export type ChatInputAreaProps = {
};

export const ChatInputArea = (props: ChatInputAreaProps) => {
const { className, onSend, inputAreaRender, inputRender } = props || {};
const { className, onSend, inputAreaRender, inputRender, sendButtonRender } = props || {};
const [
sendMessage,
isLoading,
Expand Down Expand Up @@ -106,40 +109,90 @@ export const ChatInputArea = (props: ChatInputAreaProps) => {

const prefixClass = getPrefixCls('pro-chat-input-area');

const defaultInput = (
<AutoCompleteTextArea
placeholder={placeholder || gLocaleObject(locale).placeholder}
{...inputAreaProps}
className={cx(styles.input, inputAreaProps?.className, `${prefixClass}-component`)}
value={message}
onChange={(e) => {
setMessage(e.target.value);
}}
autoSize={{ maxRows: 8 }}
onCompositionStart={() => {
isChineseInput.current = true;
}}
onCompositionEnd={() => {
isChineseInput.current = false;
}}
onPressEnter={(e) => {
if (!isLoading && !e.shiftKey && !isChineseInput.current) {
e.preventDefault();
send();
}
}}
/>
);
/**
* 默认的自动完成文本区域属性
*
* @property {string} placeholder - 输入框的占位符
* @property {Object} inputAreaProps - 输入框的其他属性
* @property {string} inputAreaProps.className - 输入框的类名
* @property {string} prefixClass - 输入框的前缀类名
* @property {string} value - 输入框的值
* @property {function} onChange - 输入框值改变时的回调函数
* @property {Object} autoSize - 输入框的自动调整大小配置
* @property {number} autoSize.maxRows - 输入框的最大行数
* @property {function} onCompositionStart - 输入法开始输入时的回调函数
* @property {function} onCompositionEnd - 输入法结束输入时的回调函数
* @property {function} onPressEnter - 按下回车键时的回调函数
*/

const defaultAutoCompleteTextAreaProps = {
placeholder: placeholder || gLocaleObject(locale).placeholder,
...inputAreaProps,
className: cx(styles.input, inputAreaProps?.className, `${prefixClass}-component`),
value: message,
onChange: (e) => {
setMessage(e.target.value);
},
autoSize: { maxRows: 8 },
onCompositionStart: () => {
isChineseInput.current = true;
},
onCompositionEnd: () => {
isChineseInput.current = false;
},
onPressEnter: (e) => {
if (!isLoading && !e.shiftKey && !isChineseInput.current) {
e.preventDefault();
send();
}
},
};

const defaultInput = <AutoCompleteTextArea {...defaultAutoCompleteTextAreaProps} />;

/**
* 支持下自定义输入框
*/
const inputDom = inputRender
? inputRender?.(defaultInput, (message) => {
sendMessage(message);
})
? inputRender?.(
defaultInput,
(message) => {
sendMessage(message);
},
defaultAutoCompleteTextAreaProps,
)
: defaultInput;

/**
* 根据 isLoading 状态返回默认的按钮道具。
* 如果 isLoading 为 true,则按钮将具有文本类型,即 stopGenerateMessage 点击处理程序,
* 和 StopLoadingIcon 作为图标。
* 如果 isLoading 为 false,则按钮将具有文本类型、发送点击处理程序、
* 和 SendOutlined 图标作为图标。
* @returns The default button props.
*/
const defaultButtonProps = useMemo(() => {
return isLoading
? ({
type: 'text',
className: styles.btn,
onClick: () => stopGenerateMessage(),
icon: <StopLoadingIcon />,
} as const)
: ({
type: 'text',
className: styles.btn,
onClick: () => send(),
icon: <SendOutlined />,
} as const);
}, [isLoading]);

const defaultButtonDom = <Button {...defaultButtonProps} />;

const buttonDom = sendButtonRender
? sendButtonRender(defaultButtonDom, defaultButtonProps)
: defaultButtonDom;

const defaultInputArea = (
<ConfigProvider
theme={{
Expand All @@ -161,21 +214,7 @@ export const ChatInputArea = (props: ChatInputAreaProps) => {
className={cx(styles.boxShadow, `${prefixClass}-text-container`)}
>
{inputDom}
{isLoading ? (
<Button
type="text"
className={styles.btn}
onClick={() => stopGenerateMessage()}
icon={<StopLoadingIcon />}
/>
) : (
<Button
type="text"
className={styles.btn}
onClick={() => send()}
icon={<SendOutlined />}
/>
)}
{buttonDom}
</Flexbox>
</Flexbox>
</ConfigProvider>
Expand Down
10 changes: 10 additions & 0 deletions src/ProChat/container/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,16 @@ export interface ConversationProps extends ProChatProps<any> {
* 输入框的渲染函数
* @param defaultDom 默认的 DOM 元素
* @param onMessageSend 发送消息的回调函数
* @param props 输入框的属性
*/
inputRender: ChatInputAreaProps['inputRender'];

/**
* 聊天发送按钮的渲染配置
* @param defaultDom 默认的 DOM 元素
* @param defaultProps 默认的属性
*/
sendButtonRender?: ChatInputAreaProps['sendButtonRender'];
}

const App = memo<ConversationProps>(
Expand All @@ -73,6 +81,7 @@ const App = memo<ConversationProps>(
inputRender,
chatItemRenderConfig,
backToBottomConfig,
sendButtonRender,
markdownProps,
}) => {
const ref = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -146,6 +155,7 @@ const App = memo<ConversationProps>(
<div ref={areaHtml}>
{
<ChatInputArea
sendButtonRender={sendButtonRender}
inputAreaRender={inputAreaRender || renderInputArea}
inputRender={inputRender}
/>
Expand Down
9 changes: 9 additions & 0 deletions src/ProChat/container/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ export interface ProChatProps<T extends Record<string, any>> extends ChatProps<T
*/
inputRender?: ConversationProps['inputRender'];

/**
* 聊天发送按钮的渲染配置
* @param defaultDom 默认的 DOM 元素
* @param defaultProps 默认的属性
*/
sendButtonRender?: ConversationProps['sendButtonRender'];

/**
* __PRO_CHAT_STORE_DEVTOOLS__ 是一个可选的布尔值或 DevtoolsOptions 对象,用于开启 ProChat 的开发者工具。
*/
Expand Down Expand Up @@ -82,6 +89,7 @@ export function ProChat<T extends Record<string, any> = Record<string, any>>({
inputRender,
markdownProps,
inputAreaRender,
sendButtonRender,
...props
}: ProChatProps<T>) {
return (
Expand All @@ -98,6 +106,7 @@ export function ProChat<T extends Record<string, any> = Record<string, any>>({
<App
chatItemRenderConfig={chatItemRenderConfig}
inputRender={inputRender}
sendButtonRender={sendButtonRender}
inputAreaRender={renderInputArea || inputAreaRender}
chatRef={props.chatRef}
showTitle={showTitle}
Expand Down

0 comments on commit 1974a3b

Please sign in to comment.