Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 29 additions & 7 deletions packages/module/src/MessageBar/MessageBar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,23 @@
&-actions {
display: flex;
justify-content: end;
padding-block-start: var(--pf-chatbot__message-bar-child--PaddingBlockStart);
padding-block-start: var(--pf-t--global--spacer--sm);
padding-block-end: var(--pf-t--global--spacer--sm);
gap: var(--pf-t--global--spacer--xs);
}

&-input {
flex: 1 1 auto;
padding-block-start: var(--pf-chatbot__message-bar-child--PaddingBlockStart);
padding-block-end: var(--pf-chatbot__message-bar-child--PaddingBlockEnd);
padding-block-start: var(--pf-t--global--spacer--sm);
padding-block-end: var(--pf-t--global--spacer--sm);
}
}

// - Form control textarea
.pf-chatbot__message-textarea {
padding-block-start: var(--pf-t--global--spacer--md);
padding-block-end: var(--pf-t--global--spacer--md);
padding-inline-start: var(--pf-t--global--spacer--md);
padding-inline-end: var(--pf-t--global--spacer--md);
--pf-v6-c-form-control--before--BorderStyle: none;
--pf-v6-c-form-control--after--BorderStyle: none;
resize: none;
background-color: transparent;
font-size: var(--pf-t--global--font--size--md);
line-height: 1.5rem;
Expand All @@ -64,4 +61,29 @@
.pf-v6-c-form-control__textarea:focus-visible {
outline: none;
}
textarea {
outline-offset: 0px;
--pf-v6-c-form-control--PaddingBlockStart: 0;
--pf-v6-c-form-control--PaddingBlockEnd: 0;
--pf-v6-c-form-control--BorderRadius: 0;
}
textarea:focus-visible {
outline: none;
}
}

@media screen and (max-width: 359px) {
.pf-chatbot__message-textarea {
margin-top: var(--pf-t--global--spacer--md) !important;
margin-bottom: var(--pf-t--global--spacer--md) !important;
}
}

.pf-chatbot--embedded {
@media screen and (max-width: 415px) {
.pf-chatbot__message-textarea {
margin-top: var(--pf-t--global--spacer--md) !important;
margin-bottom: var(--pf-t--global--spacer--md) !important;
}
}
}
159 changes: 149 additions & 10 deletions packages/module/src/MessageBar/MessageBar.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React from 'react';
import { ButtonProps, DropEvent, TextAreaProps } from '@patternfly/react-core';
import { AutoTextArea } from 'react-textarea-auto-witdth-height';
import { ButtonProps, DropEvent, TextArea, TextAreaProps } from '@patternfly/react-core';

// Import Chatbot components
import SendButton from './SendButton';
import MicrophoneButton from './MicrophoneButton';
import { AttachButton } from './AttachButton';
import AttachMenu from '../AttachMenu';
import StopButton from './StopButton';
import { ChatbotDisplayMode } from '../Chatbot';

export interface MessageBarWithAttachMenuProps {
/** Flag to enable whether attach menu is open */
Expand Down Expand Up @@ -63,6 +63,8 @@
};
/** A callback for when the text area value changes. */
onChange?: (event: React.ChangeEvent<HTMLTextAreaElement>, value: string) => void;
/** Display mode of chatbot, if you want to message bar to resize when the display mode changes */
displayMode?: ChatbotDisplayMode;
}

export const MessageBar: React.FunctionComponent<MessageBarProps> = ({
Expand All @@ -78,25 +80,157 @@
hasStopButton,
buttonProps,
onChange,
displayMode,
...props
}: MessageBarProps) => {
// Text Input
// --------------------------------------------------------------------------
const [message, setMessage] = React.useState<string>('');
const [isListeningMessage, setIsListeningMessage] = React.useState<boolean>(false);

const textareaRef = React.useRef(null);
const [hasSentMessage, setHasSentMessage] = React.useState(false);
const textareaRef = React.useRef<HTMLTextAreaElement>(null);
const attachButtonRef = React.useRef<HTMLButtonElement>(null);

const setInitialLineHeight = (field: HTMLTextAreaElement) => {
field.style.setProperty('line-height', '1rem');
const parent = field.parentElement;
if (parent) {
parent.style.setProperty('margin-top', `0rem`);
parent.style.setProperty('margin-bottom', `0rem`);
parent.style.setProperty('height', 'inherit');

const grandparent = parent.parentElement;
if (grandparent) {
grandparent.style.setProperty('flex-basis', 'auto');
}
}
};

const setAutoHeight = (field: HTMLTextAreaElement) => {
const parent = field.parentElement;
if (parent) {
parent.style.setProperty('height', 'inherit');
const computed = window.getComputedStyle(field);
// Calculate the height
const height =
parseInt(computed.getPropertyValue('border-top-width')) +
parseInt(computed.getPropertyValue('padding-top')) +
field.scrollHeight +
parseInt(computed.getPropertyValue('padding-bottom')) +
parseInt(computed.getPropertyValue('border-bottom-width'));
parent.style.setProperty('height', `${height}px`);

if (height > 32 || window.innerWidth <= 507) {
parent.style.setProperty('margin-bottom', `1rem`);
parent.style.setProperty('margin-top', `1rem`);
}
}
};

const textIsLongerThan2Lines = (field: HTMLTextAreaElement) => {
const lineHeight = parseFloat(window.getComputedStyle(field).lineHeight);
const lines = field.scrollHeight / lineHeight;
return lines > 2;
};

const setAutoWidth = (field: HTMLTextAreaElement) => {
const parent = field.parentElement;
if (parent) {
const grandparent = parent.parentElement;
if (textIsLongerThan2Lines(field) && grandparent) {
grandparent.style.setProperty('flex-basis', `100%`);
}
}
};

const handleNewLine = (field: HTMLTextAreaElement) => {
const parent = field.parentElement;
if (parent) {
parent.style.setProperty('margin-bottom', `1rem`);
parent.style.setProperty('margin-top', `1rem`);
}
};

const handleMobileHeight = (field: HTMLTextAreaElement) => {
const parent = field.parentElement;
if (parent) {
parent.style.setProperty('margin-bottom', `1rem`);
parent.style.setProperty('margin-top', `1rem`);
}
};

React.useEffect(() => {
const field = textareaRef.current;
if (field) {
if (field.value === '') {
if (window.innerWidth > 507) {
setInitialLineHeight(field);
} else {
handleMobileHeight(field);
}
} else {
setAutoHeight(field);
setAutoWidth(field);
}
}
const resetHeight = () => {
if (field) {
if (field.value === '') {
if (window.innerWidth > 507) {
setInitialLineHeight(field);
} else {
handleMobileHeight(field);
}
} else {
setAutoHeight(field);
setAutoWidth(field);
}
}
};
window.addEventListener('resize', resetHeight);

return () => {
window.removeEventListener('resize', resetHeight);
};
}, []);

Check warning on line 195 in packages/module/src/MessageBar/MessageBar.tsx

View workflow job for this annotation

GitHub Actions / call-build-lint-test-workflow / lint

React Hook React.useEffect has a missing dependency: 'setAutoWidth'. Either include it or remove the dependency array

React.useEffect(() => {
const field = textareaRef.current;
if (field) {
if (field.value === '') {
setInitialLineHeight(textareaRef.current);
} else {
setAutoHeight(textareaRef.current);
setAutoWidth(field);
}
}
}, [displayMode, message]);

Check warning on line 207 in packages/module/src/MessageBar/MessageBar.tsx

View workflow job for this annotation

GitHub Actions / call-build-lint-test-workflow / lint

React Hook React.useEffect has a missing dependency: 'setAutoWidth'. Either include it or remove the dependency array

React.useEffect(() => {
const field = textareaRef.current;
if (field) {
setInitialLineHeight(field);
setHasSentMessage(false);
}
}, [hasSentMessage]);

const handleChange = React.useCallback((event) => {
onChange && onChange(event, event.target.value);
if (textareaRef.current) {
if (event.target.value === '') {
setInitialLineHeight(textareaRef.current);
} else {
setAutoHeight(textareaRef.current);
}
}
setMessage(event.target.value);
}, []);

Check warning on line 227 in packages/module/src/MessageBar/MessageBar.tsx

View workflow job for this annotation

GitHub Actions / call-build-lint-test-workflow / lint

React Hook React.useCallback has a missing dependency: 'onChange'. Either include it or remove the dependency array. If 'onChange' changes too often, find the parent component that defines it and wrap that definition in useCallback

// Handle sending message
const handleSend = React.useCallback(() => {
setMessage((m) => {
onSendMessage(m);
setHasSentMessage(true);
return '';
});
}, [onSendMessage]);
Expand All @@ -109,8 +243,13 @@
handleSend();
}
}
if (event.key === 'Enter' && event.shiftKey) {
if (textareaRef.current) {
handleNewLine(textareaRef.current);
}
}
},
[handleSend]

Check warning on line 252 in packages/module/src/MessageBar/MessageBar.tsx

View workflow job for this annotation

GitHub Actions / call-build-lint-test-workflow / lint

React Hook React.useCallback has missing dependencies: 'hasStopButton' and 'isSendButtonDisabled'. Either include them or remove the dependency array
);

const handleAttachMenuToggle = () => {
Expand Down Expand Up @@ -173,14 +312,14 @@
const messageBarContents = (
<>
<div className="pf-chatbot__message-bar-input">
<AutoTextArea
ref={textareaRef}
<TextArea
className="pf-chatbot__message-textarea"
value={message as any} // Added any to make the third part TextArea component types happy. Remove when replced with PF TextArea
onChange={handleChange as any} // Added any to make the third part TextArea component types happy. Remove when replced with PF TextArea
onKeyDown={handleKeyDown}
placeholder={isListeningMessage ? 'Listening' : 'Send a message...'}
value={message}
onChange={handleChange}
aria-label={isListeningMessage ? 'Listening' : 'Send a message...'}
placeholder={isListeningMessage ? 'Listening' : 'Send a message...'}
ref={textareaRef}
onKeyDown={handleKeyDown}
{...props}
/>
</div>
Expand Down
Loading