From 51753615b48cf89d23ef8655e4e0d5ec9beb99cd Mon Sep 17 00:00:00 2001 From: Danny Cho Date: Thu, 23 Jan 2025 13:51:07 +1300 Subject: [PATCH] feat: improve UX on message container --- .../sdk-sample-app/src/components/Message.tsx | 153 +++++++++++++++--- 1 file changed, 132 insertions(+), 21 deletions(-) diff --git a/packages/passport/sdk-sample-app/src/components/Message.tsx b/packages/passport/sdk-sample-app/src/components/Message.tsx index 734b55226f..7bb9840d52 100644 --- a/packages/passport/sdk-sample-app/src/components/Message.tsx +++ b/packages/passport/sdk-sample-app/src/components/Message.tsx @@ -1,44 +1,155 @@ import { Sticker } from '@biom3/react'; -import { Form } from 'react-bootstrap'; -import React, { useEffect } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { useStatusProvider } from '@/context/StatusProvider'; import CardStack from '@/components/CardStack'; +const INITIAL_HEIGHT = 300; +const SCROLL_THRESHOLD = 20; +const HEIGHT_INCREMENT = 100; + function Message() { const { messages, clearMessages } = useStatusProvider(); + const messagesEndRef = useRef(null); + const scrollContainerRef = useRef(null); + const [containerHeight, setContainerHeight] = useState(INITIAL_HEIGHT); + const [isAtBottom, setIsAtBottom] = useState(true); + + const handleScroll = () => { + const container = scrollContainerRef.current; + if (!container) return; + + const isBottom = Math.abs( + container.scrollHeight - container.scrollTop - container.clientHeight, + ) < SCROLL_THRESHOLD; + setIsAtBottom(isBottom); + }; useEffect(() => { - const textarea = document.querySelector('textarea'); - if (textarea) { - textarea.scrollTop = textarea.scrollHeight; + const container = scrollContainerRef.current; + if (!container) return; + + const shouldScrollToBottom = isAtBottom + || messages.length > (parseInt(container.dataset.prevLength || '0', 10)); + + if (shouldScrollToBottom) { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); } - }, [messages]); + + container.dataset.prevLength = messages.length.toString(); + }, [messages, isAtBottom]); + + const handleExpand = () => setContainerHeight((prev) => prev + HEIGHT_INCREMENT); + const handleShrink = () => setContainerHeight((prev) => Math.max(INITIAL_HEIGHT, prev - HEIGHT_INCREMENT)); + + const baseIconStyles = { + cursor: 'pointer', + position: 'absolute', + backgroundColor: 'white', + boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)', + transition: 'all 0.2s ease', + }; + + const arrowIconStyles = { + ...baseIconStyles, + bottom: '-24px', + transform: 'translateX(-50%)', + '&:hover': { + transform: 'translateX(-50%) translateY(-2px)', + boxShadow: '0 4px 12px rgba(0, 0, 0, 0.2)', + }, + }; return ( -
- - - -
+
+
+ {messages.map((message, index) => ( +
+ {message} +
+ ))} +
+
+ + INITIAL_HEIGHT ? 1 : 0.5, + cursor: containerHeight > INITIAL_HEIGHT ? 'pointer' : 'not-allowed', + }} + onClick={handleShrink} + /> + + +
+