diff --git a/src/App.jsx b/src/App.jsx index 4b597f0..fa2a225 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -10,20 +10,30 @@ function App() { const [messages, addMessage] = useMessages([]) const [newMessage, setNewMessage] = useState('') - const handleSubmit = useCallback(() => { - if (newMessage.length > 0) { - addMessage(newMessage) - setNewMessage('') - } - }, [newMessage, messages]) + const handleSubmit = useCallback( + bubbleHeight => { + if (newMessage.length > 0) { + addMessage({ + id: +new Date(), + text: newMessage, + height: bubbleHeight + }) + setNewMessage('') + } + }, + [newMessage, messages] + ) + + const lastMessage = messages[messages.length - 1] + const dy = lastMessage ? lastMessage.height : 0 return (
{messages.map(m => ( - - {m} + + {m.text} ))} diff --git a/src/bubble-input.css b/src/bubble-input.css index eaf0e84..c1ee13d 100644 --- a/src/bubble-input.css +++ b/src/bubble-input.css @@ -1,4 +1,5 @@ -.bubble.input { +.bubble.input, +.bubble.input .bubble-content { transition: opacity 0.4s ease-in-out; opacity: 1; border: none; diff --git a/src/bubble-input.jsx b/src/bubble-input.jsx index a4f7d8a..ae2c6c3 100644 --- a/src/bubble-input.jsx +++ b/src/bubble-input.jsx @@ -3,19 +3,23 @@ import './bubble-input.css' const BubbleInput = ({ onChange, onSubmit, value }) => { const refEditable = useRef() + const refContainer = useRef() const [submitted, setSubmitted] = useState(false) const handleKeyDown = e => { + const { current: elContainer } = refContainer + const { current: elEditable } = refEditable const { isComposing } = e.nativeEvent if (e.key === 'Enter' && !isComposing) { - onSubmit && onSubmit() + const height = elContainer.clientHeight + onSubmit && onSubmit(height) e.preventDefault() setSubmitted(true) - setTimeout(() => { - refEditable.current.focus() - refEditable.current.innerText = '' + requestAnimationFrame(() => { + elEditable.focus() + elEditable.innerText = '' setSubmitted(false) - }, 10) + }) } } const handleBlur = useCallback(() => { @@ -29,16 +33,21 @@ const BubbleInput = ({ onChange, onSubmit, value }) => { return (
onChange(e.target.innerText)} - /> + > +
onChange(e.target.innerText)} + /> +
) } diff --git a/src/bubble.css b/src/bubble.css index 17792ea..c6d24f4 100644 --- a/src/bubble.css +++ b/src/bubble.css @@ -1,20 +1,25 @@ .bubble { + max-width: 600px; +} + +.bubble-content { + max-width: 600px; + display: inline-block; border-radius: 30px; padding: 12px 20px; margin-top: 5px; margin-bottom: 5px; - display: inline-block; - max-width: 600px; + hyphens: auto; } -.bubble { +.bubble-content { margin-right: 25%; background-color: #eee; position: relative; } -.bubble:last-child:before, -.bubble:nth-last-child(2):before { +.bubble:last-child .bubble-content:before, +.bubble:nth-last-child(2) .bubble-content:before { content: ''; position: absolute; z-index: 0; @@ -26,8 +31,8 @@ border-bottom-right-radius: 20px; } -.bubble:last-child:after, -.bubble:nth-last-child(2):after { +.bubble:last-child .bubble-content:after, +.bubble:nth-last-child(2) .bubble-content:after { content: ''; position: absolute; z-index: 1; diff --git a/src/bubble.jsx b/src/bubble.jsx index 4ee286c..3ae2c5e 100644 --- a/src/bubble.jsx +++ b/src/bubble.jsx @@ -1,17 +1,38 @@ import React from 'react' -import { motion } from 'framer-motion' +import { motion, usePresence } from 'framer-motion' import './bubble.css' -const Bubble = ({ id, children, sender }) => { +const transition = { + type: 'spring', + stiffness: 500, + damping: 50, + default: { + duration: 1 + } +} + +const Bubble = ({ id, children, sender, dy }) => { + const [isPresent, safeToRemove] = usePresence() + + const animations = { + layout: true, + initial: 'out', + style: { + position: 'static' + }, + animate: 'in', + variants: { + in: { opacity: 1, translateY: 0 }, + out: { opacity: 1, translateY: `${dy}px` } + }, + exit: { opacity: 0, translateY: 0 }, + onAnimationComplete: () => !isPresent && safeToRemove(), + transition + } + return ( - - {children} + +
{children}
) } diff --git a/src/chat.css b/src/chat.css index 71fbd51..f39d36e 100644 --- a/src/chat.css +++ b/src/chat.css @@ -7,4 +7,5 @@ align-items: flex-start; font-size: 32px; + position: relative; }