mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-17 14:01:25 +00:00
Share chat UI components in @sandbox-agent/react (#228)
* Extract shared chat UI components * chore(release): update version to 0.3.1 * Use shared chat UI in Foundry
This commit is contained in:
parent
6d7e67fe72
commit
0471214d65
19 changed files with 1679 additions and 727 deletions
117
sdks/react/src/ChatComposer.tsx
Normal file
117
sdks/react/src/ChatComposer.tsx
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
"use client";
|
||||
|
||||
import type { KeyboardEvent, ReactNode, Ref, TextareaHTMLAttributes } from "react";
|
||||
|
||||
export interface ChatComposerClassNames {
|
||||
root: string;
|
||||
form: string;
|
||||
input: string;
|
||||
submit: string;
|
||||
submitContent: string;
|
||||
}
|
||||
|
||||
export interface ChatComposerProps {
|
||||
message: string;
|
||||
onMessageChange: (value: string) => void;
|
||||
onSubmit: () => void;
|
||||
onKeyDown?: (event: KeyboardEvent<HTMLTextAreaElement>) => void;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
submitDisabled?: boolean;
|
||||
allowEmptySubmit?: boolean;
|
||||
submitLabel?: string;
|
||||
className?: string;
|
||||
classNames?: Partial<ChatComposerClassNames>;
|
||||
inputRef?: Ref<HTMLTextAreaElement>;
|
||||
rows?: number;
|
||||
textareaProps?: Omit<
|
||||
TextareaHTMLAttributes<HTMLTextAreaElement>,
|
||||
"className" | "disabled" | "onChange" | "onKeyDown" | "placeholder" | "rows" | "value"
|
||||
>;
|
||||
renderSubmitContent?: () => ReactNode;
|
||||
}
|
||||
|
||||
const DEFAULT_CLASS_NAMES: ChatComposerClassNames = {
|
||||
root: "sa-chat-composer",
|
||||
form: "sa-chat-composer-form",
|
||||
input: "sa-chat-composer-input",
|
||||
submit: "sa-chat-composer-submit",
|
||||
submitContent: "sa-chat-composer-submit-content",
|
||||
};
|
||||
|
||||
const cx = (...values: Array<string | false | null | undefined>) => values.filter(Boolean).join(" ");
|
||||
|
||||
const mergeClassNames = (
|
||||
defaults: ChatComposerClassNames,
|
||||
overrides?: Partial<ChatComposerClassNames>,
|
||||
): ChatComposerClassNames => ({
|
||||
root: cx(defaults.root, overrides?.root),
|
||||
form: cx(defaults.form, overrides?.form),
|
||||
input: cx(defaults.input, overrides?.input),
|
||||
submit: cx(defaults.submit, overrides?.submit),
|
||||
submitContent: cx(defaults.submitContent, overrides?.submitContent),
|
||||
});
|
||||
|
||||
export const ChatComposer = ({
|
||||
message,
|
||||
onMessageChange,
|
||||
onSubmit,
|
||||
onKeyDown,
|
||||
placeholder,
|
||||
disabled = false,
|
||||
submitDisabled = false,
|
||||
allowEmptySubmit = false,
|
||||
submitLabel = "Send",
|
||||
className,
|
||||
classNames: classNameOverrides,
|
||||
inputRef,
|
||||
rows = 1,
|
||||
textareaProps,
|
||||
renderSubmitContent,
|
||||
}: ChatComposerProps) => {
|
||||
const resolvedClassNames = mergeClassNames(DEFAULT_CLASS_NAMES, classNameOverrides);
|
||||
const isSubmitDisabled = disabled || submitDisabled || (!allowEmptySubmit && message.trim().length === 0);
|
||||
|
||||
return (
|
||||
<div className={cx(resolvedClassNames.root, className)} data-slot="root">
|
||||
<form
|
||||
className={resolvedClassNames.form}
|
||||
data-slot="form"
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault();
|
||||
if (!isSubmitDisabled) {
|
||||
onSubmit();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<textarea
|
||||
{...textareaProps}
|
||||
ref={inputRef}
|
||||
className={resolvedClassNames.input}
|
||||
data-slot="input"
|
||||
data-disabled={disabled ? "true" : undefined}
|
||||
data-empty={message.trim().length === 0 ? "true" : undefined}
|
||||
value={message}
|
||||
onChange={(event) => onMessageChange(event.target.value)}
|
||||
onKeyDown={onKeyDown}
|
||||
placeholder={placeholder}
|
||||
rows={rows}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className={resolvedClassNames.submit}
|
||||
data-slot="submit"
|
||||
data-disabled={isSubmitDisabled ? "true" : undefined}
|
||||
disabled={isSubmitDisabled}
|
||||
aria-label={submitLabel}
|
||||
title={submitLabel}
|
||||
>
|
||||
<span className={resolvedClassNames.submitContent} data-slot="submit-content">
|
||||
{renderSubmitContent?.() ?? submitLabel}
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue