Skip to content

Commit

Permalink
feat: add ChatBubble component
Browse files Browse the repository at this point in the history
  • Loading branch information
yurisldk committed Dec 9, 2022
1 parent ec58afb commit 7f4a8a1
Show file tree
Hide file tree
Showing 8 changed files with 350 additions and 0 deletions.
200 changes: 200 additions & 0 deletions src/ChatBubble/ChatBubble.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import React from 'react'
import { Story, Meta } from '@storybook/react'
import ChatBubble, { ChatBubbleProps } from '.'

const meta: Meta = {
title: `Data Display/Chat Bubble`,
component: ChatBubble,
decorators: [
(Story) => (
<div className="w-full">
<Story />
</div>
),
],
}

export default meta

interface DefaultControls {
header: boolean
time: boolean
avatar: boolean
footer: boolean
side: 'start' | 'end'
}

export const Default: Story<DefaultControls> = ({
header,
time,
avatar,
footer,
side,
}) => {
return (
<ChatBubble end={side === 'end' ? true : false}>
{header && (
<ChatBubble.Header>
Obi-Wan Kenobi{' '}
{time && <ChatBubble.Time>2 hours ago</ChatBubble.Time>}
</ChatBubble.Header>
)}
{avatar && (
<ChatBubble.Avatar src="https://placeimg.com/192/192/people" />
)}
<ChatBubble.Message>You were my brother, Anakin.</ChatBubble.Message>
{footer && <ChatBubble.Footer>Seen</ChatBubble.Footer>}
</ChatBubble>
)
}
Default.args = {
header: false,
time: false,
avatar: false,
footer: false,
side: 'start',
}
Default.argTypes = {
side: {
options: ['start', 'end'],
control: { type: 'radio' },
},
}
Default.parameters = { controls: { exclude: ['end', 'dataTheme'] } }

export const Side: Story<ChatBubbleProps> = (args) => (
<>
<ChatBubble>
<ChatBubble.Message>
It's over Anakin, <br />I have the high ground.
</ChatBubble.Message>
</ChatBubble>

<ChatBubble end>
<ChatBubble.Message>You underestimate my power!</ChatBubble.Message>
</ChatBubble>
</>
)

export const WithImage: Story<ChatBubbleProps> = (args) => (
<>
<ChatBubble>
<ChatBubble.Avatar src="https://placeimg.com/192/192/people" />
<ChatBubble.Message>
It was said that you would, destroy the Sith, not join them.
</ChatBubble.Message>
</ChatBubble>

<ChatBubble>
<ChatBubble.Avatar src="https://placeimg.com/192/192/people" />
<ChatBubble.Message>
It was you who would bring balance to the Force
</ChatBubble.Message>
</ChatBubble>

<ChatBubble>
<ChatBubble.Avatar src="https://placeimg.com/192/192/people" />
<ChatBubble.Message>Not leave it in Darkness</ChatBubble.Message>
</ChatBubble>
</>
)

export const WithHeader: Story<ChatBubbleProps> = (args) => (
<>
<ChatBubble>
<ChatBubble.Header>
Obi-Wan Kenobi <ChatBubble.Time>12:45</ChatBubble.Time>
</ChatBubble.Header>
<ChatBubble.Avatar src="https://placeimg.com/192/192/people" />
<ChatBubble.Message>You were the Chosen One!</ChatBubble.Message>
</ChatBubble>

<ChatBubble end>
<ChatBubble.Header>
Anakin <ChatBubble.Time>12:46</ChatBubble.Time>
</ChatBubble.Header>
<ChatBubble.Avatar src="https://placeimg.com/192/192/people" />
<ChatBubble.Message>I hate you!</ChatBubble.Message>
</ChatBubble>
</>
)

export const WithFooter: Story<ChatBubbleProps> = (args) => (
<>
<ChatBubble>
<ChatBubble.Avatar src="https://placeimg.com/192/192/people" />
<ChatBubble.Message>You were the Chosen One!</ChatBubble.Message>
<ChatBubble.Footer>Delivered</ChatBubble.Footer>
</ChatBubble>

<ChatBubble end>
<ChatBubble.Avatar src="https://placeimg.com/192/192/people" />
<ChatBubble.Message>I hate you!</ChatBubble.Message>
<ChatBubble.Footer>Seen at 12:46</ChatBubble.Footer>
</ChatBubble>
</>
)

export const WithHeaderAndFooter: Story<ChatBubbleProps> = (args) => (
<>
<ChatBubble>
<ChatBubble.Header>
Obi-Wan Kenobi <ChatBubble.Time>12:45</ChatBubble.Time>
</ChatBubble.Header>
<ChatBubble.Message>You were the Chosen One!</ChatBubble.Message>
<ChatBubble.Footer>Delivered</ChatBubble.Footer>
</ChatBubble>

<ChatBubble end>
<ChatBubble.Header>
Anakin <ChatBubble.Time>12:46</ChatBubble.Time>
</ChatBubble.Header>
<ChatBubble.Message>I hate you!</ChatBubble.Message>
<ChatBubble.Footer>Seen at 12:46</ChatBubble.Footer>
</ChatBubble>
</>
)

export const Colors: Story<ChatBubbleProps> = (args) => (
<>
<ChatBubble>
<ChatBubble.Message color="primary">
What kind of nonsense is this
</ChatBubble.Message>
</ChatBubble>

<ChatBubble>
<ChatBubble.Message color="secondary">
Put me on the Council and not make me a Master!??
</ChatBubble.Message>
</ChatBubble>

<ChatBubble>
<ChatBubble.Message color="accent">
That's never been done in the history of the Jedi. It's insulting!
</ChatBubble.Message>
</ChatBubble>

<ChatBubble end>
<ChatBubble.Message color="info">Calm down, Anakin.</ChatBubble.Message>
</ChatBubble>

<ChatBubble end>
<ChatBubble.Message color="success">
You have been given a great honor.
</ChatBubble.Message>
</ChatBubble>

<ChatBubble end>
<ChatBubble.Message color="warning">
To be on the Council at your age.
</ChatBubble.Message>
</ChatBubble>

<ChatBubble end>
<ChatBubble.Message color="error">
It's never happened before.
</ChatBubble.Message>
</ChatBubble>
</>
)
42 changes: 42 additions & 0 deletions src/ChatBubble/ChatBubble.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { forwardRef, ReactNode } from 'react'
import clsx from 'clsx'
import { twMerge } from 'tailwind-merge'

import { IComponentBaseProps, ComponentColor } from '../types'
import Avatar from '../Avatar'
import ChatBubbleAvatar from './ChatBubbleAvatar'
import ChatBubbleMessage from './ChatBubbleMessage'
import ChatBubbleHeader from './ChatBubbleHeader'
import ChatBubbleTime from './ChatBubbleTime'
import ChatBubbleFooter from './ChatBubbleFooter'

export type ChatBubbleProps = React.HTMLAttributes<HTMLDivElement> &
IComponentBaseProps & {
end?: boolean
}

const ChatBubble = forwardRef<HTMLDivElement, ChatBubbleProps>(
(
{ end = false, color, dataTheme, className, children, ...props },
ref
): JSX.Element => (
<div
{...props}
data-theme={dataTheme}
className={twMerge('chat', `chat-${end ? 'end' : 'start'}`, className)}
ref={ref}
>
{children}
</div>
)
)

ChatBubble.displayName = 'ChatBubble'

export default Object.assign(ChatBubble, {
Header: ChatBubbleHeader,
Time: ChatBubbleTime,
Avatar: ChatBubbleAvatar,
Message: ChatBubbleMessage,
Footer: ChatBubbleFooter,
})
21 changes: 21 additions & 0 deletions src/ChatBubble/ChatBubbleAvatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react'
import { twMerge } from 'tailwind-merge'
import { Avatar, AvatarProps } from '..'
import { IComponentBaseProps } from '../types'

export type ChatBubbleAvatarProps = AvatarProps & IComponentBaseProps

const ChatBubbleAvatar = React.forwardRef<
HTMLDivElement,
ChatBubbleAvatarProps
>(({ size = 'xs', shape = 'circle', className, ...props }, ref) => (
<Avatar
size={size}
shape={shape}
{...props}
className={twMerge('chat-image', className)}
ref={ref}
/>
))

export default ChatBubbleAvatar
21 changes: 21 additions & 0 deletions src/ChatBubble/ChatBubbleFooter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react'
import { twMerge } from 'tailwind-merge'
import { IComponentBaseProps } from '../types'

export type ChatBubbleFooterProps = React.HTMLAttributes<HTMLDivElement> &
IComponentBaseProps

const ChatBubbleFooter = React.forwardRef<
HTMLDivElement,
ChatBubbleFooterProps
>(({ className, ...props }, ref) => (
<div
{...props}
className={twMerge('chat-footer opacity-50', className)}
ref={ref}
/>
/* <time className={twMerge('text-xs opacity-50', className)}>{time}</time> */
// <div {...props} className={twMerge('chat-bubble', className)} ref={ref} />
))

export default ChatBubbleFooter
17 changes: 17 additions & 0 deletions src/ChatBubble/ChatBubbleHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react'
import { twMerge } from 'tailwind-merge'
import { IComponentBaseProps } from '../types'

export type ChatBubbleHeaderProps = React.HTMLAttributes<HTMLDivElement> &
IComponentBaseProps

const ChatBubbleHeader = React.forwardRef<
HTMLDivElement,
ChatBubbleHeaderProps
>(({ className, ...props }, ref) => (
<div {...props} className={twMerge('chat-header', className)} ref={ref} />
/* <time className={twMerge('text-xs opacity-50', className)}>{time}</time> */
// <div {...props} className={twMerge('chat-bubble', className)} ref={ref} />
))

export default ChatBubbleHeader
26 changes: 26 additions & 0 deletions src/ChatBubble/ChatBubbleMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import clsx from 'clsx'
import React from 'react'
import { twMerge } from 'tailwind-merge'
import { ComponentColor, IComponentBaseProps } from '../types'

export type ChatBubbleMessageProps = React.HTMLAttributes<HTMLDivElement> &
IComponentBaseProps & {
color?: ComponentColor
}

const ChatBubbleMessage = React.forwardRef<
HTMLDivElement,
ChatBubbleMessageProps
>(({ color, className, ...props }, ref) => {
const classes = twMerge(
'chat-bubble',
clsx({
[`chat-bubble-${color}`]: color,
}),
className
)

return <div {...props} className={classes} ref={ref} />
})

export default ChatBubbleMessage
18 changes: 18 additions & 0 deletions src/ChatBubble/ChatBubbleTime.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react'
import { twMerge } from 'tailwind-merge'
import { IComponentBaseProps } from '../types'

export type ChatBubbleTimeProps = React.TimeHTMLAttributes<HTMLTimeElement> &
IComponentBaseProps

const ChatBubbleTime = React.forwardRef<HTMLDivElement, ChatBubbleTimeProps>(
({ className, ...props }, ref) => (
<time
{...props}
className={twMerge('text-xs opacity-50', className)}
ref={ref}
/>
)
)

export default ChatBubbleTime
5 changes: 5 additions & 0 deletions src/ChatBubble/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './ChatBubble';

import ChatBubble, { ChatBubbleProps as TChatBubbleProps } from './ChatBubble'
export type ChatBubbleProps = TChatBubbleProps
export default ChatBubble

0 comments on commit 7f4a8a1

Please sign in to comment.