diff --git a/src/components/Attachment/AttachmentActions.tsx b/src/components/Attachment/AttachmentActions.tsx index 938cd031e..ced98d4cd 100644 --- a/src/components/Attachment/AttachmentActions.tsx +++ b/src/components/Attachment/AttachmentActions.tsx @@ -70,10 +70,9 @@ const UnMemoizedAttachmentActions = (props: AttachmentActionsProps) => { {text} {actions.map((action, index) => ( diff --git a/src/components/Attachment/components/PlaybackRateButton.tsx b/src/components/Attachment/components/PlaybackRateButton.tsx index 008037cb6..503b5aee3 100644 --- a/src/components/Attachment/components/PlaybackRateButton.tsx +++ b/src/components/Attachment/components/PlaybackRateButton.tsx @@ -9,7 +9,6 @@ export const PlaybackRateButton = ({ children, onClick }: PlaybackRateButtonProp className={clsx('str-chat__message_attachment__playback-rate-button')} data-testid='playback-rate-button' onClick={onClick} - type='button' > {children} diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 8637f1d20..f213cf260 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -2,10 +2,41 @@ import type { ComponentProps } from 'react'; import { forwardRef } from 'react'; import clsx from 'clsx'; -export type ButtonProps = ComponentProps<'button'>; +export type ButtonVariant = 'primary' | 'secondary' | 'danger'; +export type ButtonAppearance = 'solid' | 'outline' | 'ghost'; +export type ButtonSize = 'lg' | 'md' | 'sm'; + +export type ButtonProps = ComponentProps<'button'> & { + /** Semantic variant: primary, secondary, or danger (maps to destructive in styles). */ + variant?: ButtonVariant; + /** Visual style: solid, outline, or ghost. */ + appearance?: ButtonAppearance; + /** When true, uses full border-radius for icon-only/pill shape. */ + circular?: boolean; + /** Size: lg, md, or sm. */ + size?: ButtonSize; +}; + +const variantToClass: Record = { + danger: 'str-chat__button--destructive', + primary: 'str-chat__button--primary', + secondary: 'str-chat__button--secondary', +}; + +const appearanceToClass: Record = { + ghost: 'str-chat__button--ghost', + outline: 'str-chat__button--outline', + solid: 'str-chat__button--solid', +}; + +const sizeToClass: Record = { + lg: 'str-chat__button--size-lg', + md: 'str-chat__button--size-md', + sm: 'str-chat__button--size-sm', +}; export const Button = forwardRef(function Button( - { className, ...props }, + { appearance, circular, className, size, variant, ...props }, ref, ) { return ( @@ -13,7 +44,14 @@ export const Button = forwardRef(function Button ref={ref} type='button' {...props} - className={clsx('str-chat__button', className)} + className={clsx( + 'str-chat__button', + variant != null && variantToClass[variant], + appearance != null && appearanceToClass[appearance], + circular && 'str-chat__button--circular', + size != null && sizeToClass[size], + className, + )} /> ); }); diff --git a/src/components/Button/PlayButton.tsx b/src/components/Button/PlayButton.tsx index 459f79014..3e467a34f 100644 --- a/src/components/Button/PlayButton.tsx +++ b/src/components/Button/PlayButton.tsx @@ -9,16 +9,13 @@ export type PlayButtonProps = ComponentProps<'button'> & { export const PlayButton = ({ className, isPlaying, ...props }: PlayButtonProps) => ( diff --git a/src/components/ChatView/ChatView.tsx b/src/components/ChatView/ChatView.tsx index 51053d712..7e46393cc 100644 --- a/src/components/ChatView/ChatView.tsx +++ b/src/components/ChatView/ChatView.tsx @@ -142,14 +142,11 @@ export const ChatViewSelectorButton = ({ ...props }: ButtonProps & { Icon?: ComponentType; text?: string }) => ( diff --git a/src/components/Dialog/components/Prompt.tsx b/src/components/Dialog/components/Prompt.tsx index 42ff6870f..96de6b8b3 100644 --- a/src/components/Dialog/components/Prompt.tsx +++ b/src/components/Dialog/components/Prompt.tsx @@ -27,14 +27,12 @@ const PromptHeader = ({
{goBack && ( @@ -47,14 +45,12 @@ const PromptHeader = ({
{close && ( @@ -88,27 +84,21 @@ const PromptFooterControls = ({ children, className }: PromptFooterControlsProps const PromptFooterControlsButtonSecondary = ({ className, ...props }: ButtonProps) => ( diff --git a/src/components/MediaRecorder/AudioRecorder/AudioRecorderRecordingControls.tsx b/src/components/MediaRecorder/AudioRecorder/AudioRecorderRecordingControls.tsx index c2ccc4d71..42f07c4ef 100644 --- a/src/components/MediaRecorder/AudioRecorder/AudioRecorderRecordingControls.tsx +++ b/src/components/MediaRecorder/AudioRecorder/AudioRecorderRecordingControls.tsx @@ -4,7 +4,6 @@ import React from 'react'; import { useMessageInputContext } from '../../../context'; import { isRecording } from './recordingStateIdentity'; import { Button } from '../../Button'; -import clsx from 'clsx'; const ToggleRecordingButton = () => { const { @@ -13,16 +12,14 @@ const ToggleRecordingButton = () => { return ( @@ -41,31 +38,27 @@ export const AudioRecorderRecordingControls = () => {
{!isRecording(recordingState) && ( )} diff --git a/src/components/MediaRecorder/AudioRecorder/AudioRecordingButtonWithNotification.tsx b/src/components/MediaRecorder/AudioRecorder/AudioRecordingButtonWithNotification.tsx index 47581d3fd..e7ba6596a 100644 --- a/src/components/MediaRecorder/AudioRecorder/AudioRecordingButtonWithNotification.tsx +++ b/src/components/MediaRecorder/AudioRecorder/AudioRecordingButtonWithNotification.tsx @@ -5,7 +5,6 @@ import { useAttachmentManagerState } from '../../MessageInput'; import { useComponentContext, useMessageInputContext } from '../../../context'; import { Callout, useDialogOnNearestManager } from '../../Dialog'; import { Button } from '../../Button'; -import clsx from 'clsx'; import { IconMicrophone } from '../../Icons'; const dialogId = 'recording-permission-denied-notification'; @@ -66,15 +65,13 @@ export const DefaultStartRecordingAudioButton = forwardRef< >(function StartRecordingAudioButton(props, ref) { return ( diff --git a/src/components/MessageActions/DeleteMessageAlert.tsx b/src/components/MessageActions/DeleteMessageAlert.tsx index 254a72ef8..deebb4dea 100644 --- a/src/components/MessageActions/DeleteMessageAlert.tsx +++ b/src/components/MessageActions/DeleteMessageAlert.tsx @@ -1,6 +1,5 @@ import { Alert } from '../Dialog'; import { Button } from '../Button'; -import clsx from 'clsx'; import React from 'react'; import { useTranslationContext } from '../../context'; import type { ModalProps } from '../Modal'; @@ -22,26 +21,22 @@ export const DeleteMessageAlert = ({ onClose, onDelete }: DeleteMessageAlertProp /> diff --git a/src/components/MessageActions/MessageActions.tsx b/src/components/MessageActions/MessageActions.tsx index dab95c2ff..7e767d812 100644 --- a/src/components/MessageActions/MessageActions.tsx +++ b/src/components/MessageActions/MessageActions.tsx @@ -98,20 +98,18 @@ export const MessageActions = ({ {dropdownActionSet.length > 0 && ( <> diff --git a/src/components/MessageActions/QuickMessageActionButton.tsx b/src/components/MessageActions/QuickMessageActionButton.tsx index 1cfe3d819..249da7034 100644 --- a/src/components/MessageActions/QuickMessageActionButton.tsx +++ b/src/components/MessageActions/QuickMessageActionButton.tsx @@ -4,13 +4,10 @@ import React from 'react'; export const QuickMessageActionsButton = ({ className, ...props }: ButtonProps) => ( diff --git a/src/components/MessageInput/AttachmentPreviewList/MediaAttachmentPreview.tsx b/src/components/MessageInput/AttachmentPreviewList/MediaAttachmentPreview.tsx index 4ace7b6f4..8cead490e 100644 --- a/src/components/MessageInput/AttachmentPreviewList/MediaAttachmentPreview.tsx +++ b/src/components/MessageInput/AttachmentPreviewList/MediaAttachmentPreview.tsx @@ -109,16 +109,14 @@ export const MediaAttachmentPreview = ({ {hasRetriableError && ( diff --git a/src/components/MessageInput/AttachmentSelector/AttachmentSelector.tsx b/src/components/MessageInput/AttachmentSelector/AttachmentSelector.tsx index f0f71b218..287972e60 100644 --- a/src/components/MessageInput/AttachmentSelector/AttachmentSelector.tsx +++ b/src/components/MessageInput/AttachmentSelector/AttachmentSelector.tsx @@ -65,15 +65,12 @@ export const AttachmentSelectorButton = forwardRef diff --git a/src/components/MessageInput/RemoveAttachmentPreviewButton.tsx b/src/components/MessageInput/RemoveAttachmentPreviewButton.tsx index aeee2f4d4..ae3964bf7 100644 --- a/src/components/MessageInput/RemoveAttachmentPreviewButton.tsx +++ b/src/components/MessageInput/RemoveAttachmentPreviewButton.tsx @@ -15,17 +15,15 @@ export const RemoveAttachmentPreviewButton = ({ const { t } = useTranslationContext(); return ( diff --git a/src/components/MessageInput/SendButton.tsx b/src/components/MessageInput/SendButton.tsx index b71fbb209..9c60655b8 100644 --- a/src/components/MessageInput/SendButton.tsx +++ b/src/components/MessageInput/SendButton.tsx @@ -3,7 +3,6 @@ import { useMessageComposerHasSendableData } from './hooks'; import { useTranslationContext } from '../../context'; import { IconPaperPlane } from '../Icons'; import { Button } from '../Button'; -import clsx from 'clsx'; export type SendButtonProps = { sendMessage: (event: React.BaseSyntheticEvent) => void; @@ -14,18 +13,15 @@ export const SendButton = ({ children, sendMessage, ...rest }: SendButtonProps) const hasSendableData = useMessageComposerHasSendableData(); return ( diff --git a/src/components/MessageList/UnreadMessagesNotification.tsx b/src/components/MessageList/UnreadMessagesNotification.tsx index c2e675dab..c49dcb7d2 100644 --- a/src/components/MessageList/UnreadMessagesNotification.tsx +++ b/src/components/MessageList/UnreadMessagesNotification.tsx @@ -35,18 +35,16 @@ export const UnreadMessagesNotification = ({ data-testid='unread-messages-notification' > -
diff --git a/src/components/MessageList/UnreadMessagesSeparator.tsx b/src/components/MessageList/UnreadMessagesSeparator.tsx index 245481ed3..5dcc158d4 100644 --- a/src/components/MessageList/UnreadMessagesSeparator.tsx +++ b/src/components/MessageList/UnreadMessagesSeparator.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { useChannelActionContext, useTranslationContext } from '../../context'; import { Button } from '../Button'; -import clsx from 'clsx'; import { IconCrossMedium } from '../Icons'; export const UNREAD_MESSAGE_SEPARATOR_CLASS = 'str-chat__unread-messages-separator'; @@ -34,13 +33,11 @@ export const UnreadMessagesSeparator = ({ : t('Unread messages')} diff --git a/src/components/MessageList/VirtualizedMessageList.tsx b/src/components/MessageList/VirtualizedMessageList.tsx index 476e7627a..201b13c16 100644 --- a/src/components/MessageList/VirtualizedMessageList.tsx +++ b/src/components/MessageList/VirtualizedMessageList.tsx @@ -576,13 +576,13 @@ const VirtualizedMessageListWithContext = ( newMessageCount={channelUnreadUiState?.unread_messages} showNotification={newMessagesNotification || hasMoreNewer} /> + - {TypingIndicator && } diff --git a/src/components/Modal/CloseButtonOnModalOverlay.tsx b/src/components/Modal/CloseButtonOnModalOverlay.tsx index d7671016d..164fe4ba5 100644 --- a/src/components/Modal/CloseButtonOnModalOverlay.tsx +++ b/src/components/Modal/CloseButtonOnModalOverlay.tsx @@ -9,13 +9,10 @@ export const CloseButtonOnModalOverlay = ({ ...props }: ComponentProps<'button'>) => ( diff --git a/src/components/Poll/PollActions/PollAction.tsx b/src/components/Poll/PollActions/PollAction.tsx index e84a5096e..34d5341eb 100644 --- a/src/components/Poll/PollActions/PollAction.tsx +++ b/src/components/Poll/PollActions/PollAction.tsx @@ -31,14 +31,13 @@ export const PollAction = ({ return ( <> diff --git a/src/components/Poll/PollCreationDialog/OptionFieldSet.tsx b/src/components/Poll/PollCreationDialog/OptionFieldSet.tsx index afc0b56da..712ffacb4 100644 --- a/src/components/Poll/PollCreationDialog/OptionFieldSet.tsx +++ b/src/components/Poll/PollCreationDialog/OptionFieldSet.tsx @@ -114,13 +114,11 @@ export const OptionFieldSet = () => { const RemoveOptionButton = ({ className, ...props }: ButtonProps) => ( + ); }; diff --git a/src/components/VideoPlayer/VideoThumbnail.tsx b/src/components/VideoPlayer/VideoThumbnail.tsx index 9cef99711..2ae5ec9a1 100644 --- a/src/components/VideoPlayer/VideoThumbnail.tsx +++ b/src/components/VideoPlayer/VideoThumbnail.tsx @@ -24,15 +24,15 @@ export const VideoThumbnail = ({ /> {onPlay ? ( diff --git a/src/plugins/Emojis/EmojiPicker.tsx b/src/plugins/Emojis/EmojiPicker.tsx index 3af1815c4..974e74b9b 100644 --- a/src/plugins/Emojis/EmojiPicker.tsx +++ b/src/plugins/Emojis/EmojiPicker.tsx @@ -9,7 +9,6 @@ import { useMessageComposer, } from '../../components'; import { usePopoverPosition } from '../../components/Dialog/hooks/usePopoverPosition'; -import clsx from 'clsx'; import { useIsCooldownActive } from '../../components/MessageInput/hooks/useIsCooldownActive'; const isShadowRoot = (node: Node): node is ShadowRoot => !!(node as ShadowRoot).host; @@ -35,14 +34,12 @@ export type EmojiPickerProps = { popperOptions?: Partial<{ placement: PopperLikePlacement }>; }; -const classNames: EmojiPickerProps = { - buttonClassName: clsx( - 'str-chat__emoji-picker-button', - 'str-chat__button--ghost', - 'str-chat__button--secondary', - 'str-chat__button--size-sm', - 'str-chat__button--circular', - ), +const defaultButtonClassName = 'str-chat__emoji-picker-button'; + +const classNames: Pick< + EmojiPickerProps, + 'pickerContainerClassName' | 'wrapperClassName' +> = { pickerContainerClassName: 'str-chat__message-textarea-emoji-picker-container', wrapperClassName: 'str-chat__message-textarea-emoji-picker', }; @@ -68,7 +65,7 @@ export const EmojiPicker = (props: EmojiPickerProps) => { refs.setFloating(popperElement); }, [popperElement, refs]); - const { buttonClassName, pickerContainerClassName, wrapperClassName } = classNames; + const { pickerContainerClassName, wrapperClassName } = classNames; const { ButtonIconComponent = IconEmojiSmile } = props; @@ -118,13 +115,17 @@ export const EmojiPicker = (props: EmojiPickerProps) => { )}