diff --git a/web/chat/chat-constants.js b/web/chat/chat-constants.js index 549ccf436..4328ced8d 100644 --- a/web/chat/chat-constants.js +++ b/web/chat/chat-constants.js @@ -1,27 +1,55 @@ // @flow +import { messageKey } from 'lib/shared/message-utils.js'; +import type { MessageInfo } from 'lib/types/message-types.js'; + +import type { ComposedMessageID } from './composed-message.react.js'; + export const tooltipStyle = { paddingLeft: 5, paddingRight: 5, rowGap: 3, }; export const tooltipLabelStyle = { padding: 6, height: 20, }; export const tooltipButtonStyle = { paddingLeft: 6, paddingRight: 6, width: 30, height: 38, }; export const typeaheadStyle = { tooltipWidth: 296, tooltipMaxHeight: 268, tooltipVerticalPadding: 16, tooltipLeftOffset: 16, tooltipTopOffset: 4, rowHeight: 40, }; + +export const getComposedMessageID = ( + messageInfo: MessageInfo, +): ComposedMessageID => { + return `ComposedMessageBox-${messageKey(messageInfo)}`; +}; + +export const defaultMaxTextAreaHeight = 150; + +// The editBoxBottomRowHeight is the height of the bottom row in the edit box +// which is the height of the buttons in the bottom row. +export const editBoxBottomRowHeight = 22; + +// The editBoxHeight is a height of the all elements of the edit box +// except for the textarea. +// It consists of: +// - 2 * 10px: .editMessage padding (edit-text-message.css) +// - 10px: .bottomRow padding between the bottom row buttons +// and the textarea (edit-text-message.css) +// - 2 * 8px: .inputBarTextInput padding (chat-input-bar.css) +// - 22px: height of the bottom row in the edit box (explained above) +// - textarea height which is NOT included here +export const editBoxHeight: number = 3 * 10 + 2 * 8 + editBoxBottomRowHeight; diff --git a/web/chat/chat-input-bar.css b/web/chat/chat-input-bar.css index 827214de6..aa34247dc 100644 --- a/web/chat/chat-input-bar.css +++ b/web/chat/chat-input-bar.css @@ -1,85 +1,86 @@ div.inputBar { display: flex; flex-direction: column; border-top: 1px solid var(--border-color); } div.inputBarWrapper { padding: 16px; display: flex; flex-direction: row; align-items: center; } div.inputBarTextInput { display: flex; background: var(--text-input-bg); border-radius: 8px; + /* Related to editBoxHeight in the `edit-text-message` component */ padding: 8px; align-items: center; flex-grow: 1; } div.inputBarTextInput > textarea { flex: 1; display: flex; border: none; resize: none; background: var(--text-input-bg); color: var(--fg); } div.inputBarTextInput > textarea:focus { outline: none; } div.joinButtonContainer { background: var(--join-bg); padding-top: 8px; padding-bottom: 8px; display: flex; flex-direction: row; width: 100%; justify-content: center; } p.joinButtonText { font-weight: var(--semi-bold); padding-left: 8px; } span.explanation { color: var(--permission-color); text-align: center; padding-top: 20px; padding-bottom: 8px; } a.multimediaUpload { cursor: pointer; position: relative; padding-right: 12px; } a.multimediaUpload > input[type='file'] { visibility: hidden; position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: 0; padding: 0; } a.multimediaUpload:hover > svg { opacity: 0.8; } div.previews { display: flex; overflow-x: auto; white-space: nowrap; } div.previews > span.multimedia { margin: 10px; } div.previews > span.multimedia > span.multimediaImage > img { max-height: 200px; max-width: 200px; } a.sendButton { padding: 8px 0 0 10px; cursor: pointer; } diff --git a/web/chat/chat-input-text-area.react.js b/web/chat/chat-input-text-area.react.js index 5fc3ee7fe..fd97e985a 100644 --- a/web/chat/chat-input-text-area.react.js +++ b/web/chat/chat-input-text-area.react.js @@ -1,114 +1,117 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; +import { defaultMaxTextAreaHeight } from './chat-constants.js'; import css from './chat-input-bar.css'; type Props = { +send?: () => mixed, +escape?: () => void, +focus: boolean, +currentText: string, +setCurrentText: (text: string) => void, +onChangePosition: () => void, + +maxHeight?: number, }; const ChatInputTextArea: React.ComponentType = React.memo( function ChatInputTextArea(props: Props) { const { currentText, focus, escape, send, setCurrentText, onChangePosition, + maxHeight = defaultMaxTextAreaHeight, } = props; const textareaRef = React.useRef(null); const focusAndUpdateText = React.useCallback(() => { if (!focus) { return; } // We need to call focus() first on Safari, otherwise the cursor // ends up at the start instead of the end for some reason const textarea = textareaRef.current; invariant(textarea, 'textarea should be set'); textarea.focus(); // We reset the textarea to an empty string at the start so that // the cursor always ends up at the end, even if the text doesn't // actually change textarea.value = ''; if (currentText) { textarea.value = currentText; } // The above strategies make sure the cursor is at the end, // but we also need to make sure that we're scrolled to the bottom textarea.scrollTop = textarea.scrollHeight; }, [currentText, focus]); const updateHeight = React.useCallback(() => { const textarea = textareaRef.current; if (textarea) { textarea.style.height = 'auto'; - const newHeight = Math.min(textarea.scrollHeight, 150); + const newHeight = Math.min(textarea.scrollHeight, maxHeight); textarea.style.height = `${newHeight}px`; } onChangePosition(); - }, [onChangePosition]); + }, [maxHeight, onChangePosition]); React.useEffect(() => { focusAndUpdateText(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); React.useEffect(() => { updateHeight(); // We want to update the height when the text changes. We can't include // updateHeight in the dep list because it causes a stack overflow... see // comments on D8035 for more details // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentText]); const onKeyDown = (event: SyntheticKeyboardEvent) => { if (event.key === 'Escape') { event.preventDefault(); if (!escape) { return; } escape(); } else if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); if (!send) { return; } send(); } }; const onChangeMessageText = ( event: SyntheticEvent, ) => { setCurrentText(event.currentTarget.value); updateHeight(); }; return (