Skip to content

Commit

Permalink
✨ feat: Enhance functionality and usability of ChatInputArea component
Browse files Browse the repository at this point in the history
- Import additional dependencies
- Add new props and their descriptions
- Update component logic
- Add new event handlers

These changes improve the ChatInputArea component.
  • Loading branch information
canisminor1990 committed Jul 10, 2023
1 parent c5fbc14 commit 6c36a25
Showing 1 changed file with 130 additions and 86 deletions.
216 changes: 130 additions & 86 deletions src/ChatInputArea/index.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,50 @@
import { Button } from 'antd';
import { Maximize2, Minimize2 } from 'lucide-react';
import { ReactNode, memo, useCallback, useEffect, useRef, useState } from 'react';
import {
CSSProperties,
ReactNode,
forwardRef,
memo,
useCallback,
useEffect,
useRef,
useState,
} from 'react';

import ActionIcon from '@/ActionIcon';
import { TextArea } from '@/Input';
import type { DivProps } from '@/types';
import { TextArea, type TextAreaProps } from '@/Input';

import { useStyles } from './style';

export interface ChatInputAreaProps extends DivProps {
export interface ChatInputAreaProps extends TextAreaProps {
/**
* @description Actions to be displayed in the left of actions bar
* @description Actions to be displayed in the input area
*/
actions?: ReactNode;
/**
* @description Default value of the input
* @description Additional class name for the component
*/
className?: string;
/**
* @description Default value for the input area
*/
defaultValue?: string;
/**
* @description Whether the input is disabled or not
* @description Whether the input area is disabled
* @default false
*/
disabled?: boolean;
/**
* @description Whether the input is expanded or not
* @description Whether the input area is expanded
* @default false
*/
expand?: boolean;
/**
* @description Footer to be displayed below input area
* @description Footer content to be displayed below the input area
*/
footer?: ReactNode;
/**
* @description Whether the input is in loading state or not
* @description Whether the input area is in loading state
* @default false
*/
loading?: boolean;
Expand All @@ -43,104 +55,136 @@ export interface ChatInputAreaProps extends DivProps {
minHeight?: number;
/**
* @description Callback function when the expand state changes
* @param expand - Whether the input is expanded or not
* @param expand - Whether the input area is expanded
*/
onExpandChange?: (expand: boolean) => void;
/**
* @description Callback function when the input value changes
* @param value - The current value of the input
* @param value - The current value of the input area
*/
onInputChange?: (value: string) => void;
/**
* @description Callback function when the send button is clicked
* @param value - The current value of the input
* @param value - The current value of the input area
*/
onSend?: (value: string) => void;
/**
* @description Placeholder text of the input
* @description Placeholder text for the input area
* @default 'Type something to chat...'
*/
placeholder?: string;
/**
* @description CSS styles for the component
*/
style?: CSSProperties;
/**
* @description Additional class name for the textarea element
*/
textareaClassName?: string;
/**
* @description CSS styles for the textarea element
*/
textareaStyle?: CSSProperties;
}

const ChatInputArea = memo<ChatInputAreaProps>(
({
minHeight = 200,
className,
actions,
footer,
expand,
placeholder = 'Type something to chat...',
onExpandChange,
onSend,
defaultValue = '',
loading,
style,
disabled,
onInputChange,
...props
}) => {
const isChineseInput = useRef(false);
const [value, setValue] = useState<string>(defaultValue);
const { cx, styles } = useStyles();
forwardRef(
(
{
textareaClassName,
style,
textareaStyle,
minHeight = 200,
className,
actions,
footer,
expand,
placeholder = 'Type something to chat...',
onExpandChange,
onSend,
defaultValue = '',
loading,
disabled,
onInputChange,
onPressEnter,
onCompositionStart,
onCompositionEnd,
onBlur,
onChange,
...props
},
reference,
) => {
const isChineseInput = useRef(false);
const [value, setValue] = useState<string>(defaultValue);
const { cx, styles } = useStyles();

const handleExpandClick = useCallback(() => {
if (onExpandChange) onExpandChange(!expand);
}, [expand]);
const handleExpandClick = useCallback(() => {
if (onExpandChange) onExpandChange(!expand);
}, [expand]);

const handleSend = useCallback(() => {
if (disabled) return;
if (onSend) onSend(value);
setValue('');
}, [disabled, value]);
const handleSend = useCallback(() => {
if (disabled) return;
if (onSend) onSend(value);
setValue('');
}, [disabled, value]);

useEffect(() => {
if (onInputChange) onInputChange(value);
}, [value]);
useEffect(() => {
if (onInputChange) onInputChange(value);
}, [value]);

return (
<section
className={cx(styles.container, className)}
style={{ minHeight, ...style }}
{...props}
>
<div className={styles.actionsBar}>
<div className={styles.actionLeft}>{actions}</div>
<div className={styles.actionsRight}>
<ActionIcon icon={expand ? Minimize2 : Maximize2} onClick={handleExpandClick} />
return (
<section className={cx(styles.container, className)} style={{ minHeight, ...style }}>
<div className={styles.actionsBar}>
<div className={styles.actionLeft}>{actions}</div>
<div className={styles.actionsRight}>
<ActionIcon icon={expand ? Minimize2 : Maximize2} onClick={handleExpandClick} />
</div>
</div>
<TextArea
className={cx(styles.textarea, textareaClassName)}
defaultValue={defaultValue}
ref={reference}
style={textareaStyle}
{...props}
onBlur={(e) => {
if (onBlur) onBlur(e);
setValue(e.target.value);
}}
onChange={(e) => {
if (onChange) onChange(e);
setValue(e.target.value);
}}
onCompositionEnd={(e) => {
if (onCompositionEnd) onCompositionEnd(e);
isChineseInput.current = false;
}}
onCompositionStart={(e) => {
if (onCompositionStart) onCompositionStart(e);
isChineseInput.current = true;
}}
onPressEnter={(e) => {
if (onPressEnter) onPressEnter(e);
if (!loading && !e.shiftKey && !isChineseInput.current) {
e.preventDefault();
handleSend();
}
}}
placeholder={placeholder}
resize={false}
type="pure"
value={value}
/>
<div className={styles.footerBar}>
{footer}
<Button disabled={disabled} loading={loading} onClick={handleSend} type="primary">
Send
</Button>
</div>
</div>
<TextArea
className={styles.textarea}
defaultValue={defaultValue}
onBlur={(e) => setValue(e.target.value)}
onChange={(e) => setValue(e.target.value)}
onCompositionEnd={() => {
isChineseInput.current = false;
}}
onCompositionStart={() => {
isChineseInput.current = true;
}}
onPressEnter={(e) => {
if (!loading && !e.shiftKey && !isChineseInput.current) {
e.preventDefault();
handleSend();
}
}}
placeholder={placeholder}
resize={false}
type="pure"
value={value}
/>
<div className={styles.footerBar}>
{footer}
<Button disabled={disabled} loading={loading} onClick={handleSend} type="primary">
Send
</Button>
</div>
</section>
);
},
</section>
);
},
),
);

export default ChatInputArea;

1 comment on commit 6c36a25

@vercel
Copy link

@vercel vercel bot commented on 6c36a25 Jul 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

lobe-ui – ./

lobe-ui-git-master-lobehub.vercel.app
lobe-ui.vercel.app
lobe-ui-lobehub.vercel.app
ui.lobehub.com

Please sign in to comment.