diff --git a/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/ChatbotFooter/ChatbotFooter.md b/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/ChatbotFooter/ChatbotFooter.md index a464b6b..3b6a23d 100644 --- a/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/ChatbotFooter/ChatbotFooter.md +++ b/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/ChatbotFooter/ChatbotFooter.md @@ -60,6 +60,16 @@ Attachments can also be added to the chatbot via [drag and drop.](/patternfly-ai ``` +### Message bar with stop button + +If you are using streaming, you may wish to allow users to stop a response from a chatbot. + +Setting `hasStopButton` to true and passing in a `handleStopButton` callback function will enable the stop button. You can use this callback to trigger an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) configured as part of your API call. + +```js file="./ChatbotMessageBarStop.tsx" + +``` + ### Footer with message bar and footnote A simple footer with a message bar and footnote would have this code structure: @@ -74,3 +84,7 @@ A simple footer with a message bar and footnote would have this code structure: ```js file="./ChatbotFooter.tsx" ``` + +``` + +``` diff --git a/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/ChatbotFooter/ChatbotMessageBarStop.tsx b/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/ChatbotFooter/ChatbotMessageBarStop.tsx new file mode 100644 index 0000000..f94f2d8 --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/ChatbotFooter/ChatbotMessageBarStop.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { MessageBar } from '@patternfly/virtual-assistant/dist/dynamic/MessageBar'; + +export const ChatbotMessageBarStop: React.FunctionComponent = () => { + const handleSend = (message) => alert(message); + + const handleStopButton = () => alert('Stop button clicked'); + + return ; +}; diff --git a/packages/module/src/MessageBar/MessageBar.scss b/packages/module/src/MessageBar/MessageBar.scss index 7640a74..87c80fa 100644 --- a/packages/module/src/MessageBar/MessageBar.scss +++ b/packages/module/src/MessageBar/MessageBar.scss @@ -2,6 +2,7 @@ @import './AttachButton'; @import './MicrophoneButton'; @import './SendButton'; +@import './StopButton'; // ============================================================================ // Chatbot Footer - Message Bar diff --git a/packages/module/src/MessageBar/MessageBar.tsx b/packages/module/src/MessageBar/MessageBar.tsx index a9e1d7c..88ab46b 100644 --- a/packages/module/src/MessageBar/MessageBar.tsx +++ b/packages/module/src/MessageBar/MessageBar.tsx @@ -7,6 +7,7 @@ import SendButton from './SendButton'; import MicrophoneButton from './MicrophoneButton'; import { AttachButton } from './AttachButton'; import AttachMenu from '../AttachMenu'; +import StopButton from './StopButton'; export interface MessageBarWithAttachMenuProps { /** Flag to enable whether attach menu is open */ @@ -40,6 +41,10 @@ export interface MessageBarProps extends TextAreaProps { hasAttachButton?: boolean; /** Flag to enable the Microphone button */ hasMicrophoneButton?: boolean; + /** Flag to enable the Stop button, used for streaming content */ + hasStopButton?: boolean; + /** Callback function for when stop button is clicked */ + handleStopButton?: () => void; /** Callback function for when attach button is used to upload a file */ handleAttach?: (data: File[], event: DropEvent) => void; /** Props to enable a menu that opens when the Attach button is clicked, instead of the attachment window */ @@ -57,6 +62,8 @@ export const MessageBar: React.FunctionComponent = ({ handleAttach, attachMenuProps, isSendButtonDisabled, + handleStopButton, + hasStopButton, ...props }: MessageBarProps) => { // Text Input @@ -83,7 +90,7 @@ export const MessageBar: React.FunctionComponent = ({ (event) => { if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); - if (!isSendButtonDisabled) { + if (!isSendButtonDisabled && !hasStopButton) { handleSend(); } } @@ -96,21 +103,12 @@ export const MessageBar: React.FunctionComponent = ({ attachMenuProps?.onAttachMenuToggleClick(); }; - const messageBarContents = ( - <> -
- -
-
+ const renderButtons = () => { + if (hasStopButton && handleStopButton) { + return ; + } + return ( + <> {attachMenuProps && ( )} @@ -127,7 +125,25 @@ export const MessageBar: React.FunctionComponent = ({ {(alwayShowSendButton || message) && ( )} + + ); + }; + + const messageBarContents = ( + <> +
+
+
{renderButtons()}
); diff --git a/packages/module/src/MessageBar/StopButton.scss b/packages/module/src/MessageBar/StopButton.scss new file mode 100644 index 0000000..e71f8cd --- /dev/null +++ b/packages/module/src/MessageBar/StopButton.scss @@ -0,0 +1,22 @@ +// ============================================================================ +// Chatbot Footer - Message Bar - Stop +// ============================================================================ +.pf-v6-c-button.pf-chatbot__button--stop { + background-color: var(--pf-t--global--color--brand--default); + border-radius: var(--pf-t--global--border--radius--pill); + padding: var(--pf-t--global--spacer--md); + width: 3rem; + height: 3rem; + display: flex; + justify-content: center; + align-items: center; + + .pf-v6-c-button__icon { + color: var(--pf-t--global--icon--color--inverse); + } + + &:hover, + &:focus { + background-color: var(--pf-t--global--color--brand--hover); + } +} diff --git a/packages/module/src/MessageBar/StopButton.tsx b/packages/module/src/MessageBar/StopButton.tsx new file mode 100644 index 0000000..39a3def --- /dev/null +++ b/packages/module/src/MessageBar/StopButton.tsx @@ -0,0 +1,57 @@ +// ============================================================================ +// Chatbot Footer - Message Bar - Send +// ============================================================================ +import React from 'react'; + +// Import PatternFly components +import { Button, ButtonProps, Tooltip, TooltipProps, Icon } from '@patternfly/react-core'; + +export interface StopButtonProps extends ButtonProps { + /** Callback for when button is clicked */ + onClick: () => void; + /** Class name for the stop button */ + className?: string; + /** Props to control the PF Tooltip component */ + tooltipProps?: TooltipProps; + /** English text "Stop" used in the tooltip */ + tooltipContent?: string; +} + +export const StopButton: React.FunctionComponent = ({ + className, + onClick, + tooltipProps, + tooltipContent = 'Send', + ...props +}: StopButtonProps) => ( + +