From 0fe223915cf20eaf8b1db08e68966edfdd08178d Mon Sep 17 00:00:00 2001 From: andrepat0 Date: Fri, 15 Nov 2024 14:14:05 +0100 Subject: [PATCH] feat: mobile responsivness for zoomed full body and fullchat layouts --- src/components/layouts/FullPage.tsx | 12 +- src/components/layouts/ZoomedFullBody.tsx | 58 +++- .../__snapshots__/FullPage.test.tsx.snap | 168 ++++++----- .../ZoomedFullBody.test.tsx.snap | 126 ++++---- src/components/layouts/zoomed-full-body.css | 280 +++++++++++++++++- src/components/ui/Spin.css | 20 ++ 6 files changed, 507 insertions(+), 157 deletions(-) diff --git a/src/components/layouts/FullPage.tsx b/src/components/layouts/FullPage.tsx index e28c0f6e..932c95a5 100644 --- a/src/components/layouts/FullPage.tsx +++ b/src/components/layouts/FullPage.tsx @@ -25,20 +25,22 @@ const FullPageLayout: React.FC = ({ {integrationStyle} {integrationBackground} - + {showInstruct && ChangeMode && changeModeProps && ( )} - {Header && headerProps &&
} +
+ {Header && headerProps &&
} +
-
+
{Avatar && avatarProps && }
-
+
{sessionId && hasUserActivatedSpeak && Chat && chatProps ? ( ) : startPanelProps ? ( @@ -46,7 +48,7 @@ const FullPageLayout: React.FC = ({ ) : null}
- {poweredBy} +
{poweredBy}
diff --git a/src/components/layouts/ZoomedFullBody.tsx b/src/components/layouts/ZoomedFullBody.tsx index 77608e44..a1194f7a 100644 --- a/src/components/layouts/ZoomedFullBody.tsx +++ b/src/components/layouts/ZoomedFullBody.tsx @@ -21,36 +21,78 @@ const ZoomedFullBodyLayout: React.FC = ({ loading = false, poweredBy, }) => { - useEffect(() => { + // Prevent body scrolling document.body.style.overflow = 'hidden'; - return () => { - document.body.style.overflow = ''; + + // Touch focus handler for chat bubbles + const handleBubbleTouch = (e: any) => { + const bubble = e.target.closest('.memori-chat--bubble'); + if (bubble) { + // Remove focus from other bubbles + document.querySelectorAll('.memori-chat--bubble.focused').forEach(other => { + if (other !== bubble) { + other.classList.remove('focused'); + } + }); + bubble.classList.add('focused'); + // Prevent any default touch behaviors + e.stopPropagation(); + } + }; + + // Handle clicking outside to remove focus + const handleOutsideTouch = (e: any) => { + const chatContainer = e.target.closest('.memori-full-body-layout--controls'); + const bubble = e.target.closest('.memori-chat--bubble'); + + if (chatContainer && !bubble) { + document.querySelectorAll('.memori-chat--bubble.focused').forEach(bubble => { + bubble.classList.remove('focused'); + }); + } }; - }, []); + // Add event listeners + const chatContainer = document.querySelector('.memori-full-body-layout--controls'); + if (chatContainer) { + chatContainer.addEventListener('touchstart', handleBubbleTouch, { passive: true }); + document.addEventListener('touchstart', handleOutsideTouch, { passive: true }); + } + + // Cleanup function + return () => { + document.body.style.overflow = ''; + if (chatContainer) { + chatContainer.removeEventListener('touchstart', handleBubbleTouch); + document.removeEventListener('touchstart', handleOutsideTouch); + } + }; + }, []); // Empty dependency array since we only want this to run once on mount return ( <> {integrationStyle} {integrationBackground} - + {showInstruct && ChangeMode && changeModeProps && ( )} - {Header && headerProps &&
} +
+ {Header && headerProps &&
} +
-
+
{Avatar && avatarProps && ( )}
-
+
{sessionId && hasUserActivatedSpeak && Chat && chatProps ? ( ) : startPanelProps ? ( diff --git a/src/components/layouts/__snapshots__/FullPage.test.tsx.snap b/src/components/layouts/__snapshots__/FullPage.test.tsx.snap index 23864b8b..918dfdcb 100644 --- a/src/components/layouts/__snapshots__/FullPage.test.tsx.snap +++ b/src/components/layouts/__snapshots__/FullPage.test.tsx.snap @@ -36,74 +36,48 @@ exports[`renders FullPage layout unchanged 1`] = ` />
- -
- -
+ +
+ +
+
+ +
- -

- - Powered by - - - Memori.AI - -

+
+ +

+ + Powered by + + + Memori.AI + +

+
- -
- -
+ +
+ +
+
+ +
div { + overflow: visible !important; + width: auto !important; + height: 80vh !important; + max-height: 80vh; + border-radius: 0; + transform: scale(1.7) translate(0px, 10vh); + will-change: transform; + } + + .memori-full-body-layout--avatar-mobile .memori--avatar-wrapper canvas { + width: auto !important; + max-width: 100%; + height: 100% !important; + max-height: 100%; + touch-action: none; + } + + .memori-full-body-layout--avatar-mobile .memori--avatar-wrapper > div canvas + div, + .memori-full-body-layout--avatar-mobile .memori--avatar-wrapper .avatar-loader { + position: absolute !important; + width: 100%; + height: 100%; + transform: none !important; + } + + .memori-full-body-layout--avatar-mobile .memori--avatar-wrapper .avatar-loader { + display: flex; + align-items: center; + justify-content: center; + } + + /* Grid and Controls */ + .memori-full-body-grid-column-right { + position: fixed; + z-index: 2; + bottom: 0; + width: 100%; + max-width: 93%; + margin-bottom: 5%; + } + + .memori--grid-column--zoomed-full-body { + height: 450px; + /* padding: 12px; + margin-bottom: 32px; */ + } + + .memori-chat--history { + max-height: 100%; + } + + /* Improved touch targets */ + .memori-control-button { + min-width: 44px; + min-height: 44px; + padding: 12px; + margin: 4px; + touch-action: manipulation; + } + + /* Header */ + .memori-full-body--header { + padding: 16px; + text-align: right; + touch-action: manipulation; + } + + .memori-header { + position: relative; + z-index: 100; + display: inline-flex; + width: auto; + height: auto; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.25rem; + text-align: center; + touch-action: manipulation; + } + + /* Hide elements */ + .memori-chat--cover, + .memori--avatar-toggle { + display: none; + } + + /* Layout adjustments */ + .memori--powered-by { + top: 0; + bottom: auto; + } + + .memori--cover, + .memori--start-panel { + max-height: 100%; + padding: 0px; + background: none; + } + + /* Chat layout */ + .memori-full-body-layout--controls { + position: relative; + z-index: 5; + display: flex; + width: 100%; + height: 100%; + flex-direction: column; + justify-content: flex-end; + touch-action: pan-y; + } + + /* Improved scrolling container */ + .memori-chat--content { + padding-bottom: env(safe-area-inset-bottom); + -webkit-overflow-scrolling: touch; + overscroll-behavior: contain; + scroll-behavior: smooth; + } + + .memori-full-body-layout--controls .memori-chat--history, + .memori-full-body-layout--controls .memori-chat--content { + padding: 0; + background: transparent; + -webkit-overflow-scrolling: touch; + overscroll-behavior: contain; + } + + .memori-full-body-layout--controls .memori-chat--history { + margin-top: 0; + backdrop-filter: none; + } + + .memori--start-panel { + padding: 24px 12px; + -webkit-overflow-scrolling: touch; + overflow-y: auto; + } + + .memori--start-panel .memori-full-body-layout--controls, + .memori-full-body-layout--controls .memori-chat--wrapper { + width: 100%; + max-width: 800px; + margin: 0 auto; + touch-action: pan-y; + } + .memori-full-body-layout--controls .memori-chat--wrapper { + height: calc(100% - 1rem); + } + + .memori-full-body-layout--controls .memori-chat--bubble, + .memori-full-body-layout--controls .memori-chat--bubble.memori-chat--user-bubble { + position: relative; + opacity: 0.6; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0.1); + touch-action: manipulation; + transition: all 0.3s ease-in-out; + user-select: text; + } + + /* Focus state */ + .memori-full-body-layout--controls .memori-chat--bubble.focused, + .memori-full-body-layout--controls .memori-chat--bubble.memori-chat--user-bubble.focused { + background-color: #fff; + box-shadow: 0 0 0 1px rgba(var(--memori-primary-rgb, 0, 0, 0), 0.1); + opacity: 1; + transform: scale(1); + } + + /* Touch active state */ + @media (hover: none) { + .memori-full-body-layout--controls .memori-chat--bubble:active, + .memori-full-body-layout--controls .memori-chat--bubble.memori-chat--user-bubble:active { + opacity: 1; + transform: scale(0.98); + } + + /* Maintain focus styles during touch */ + .memori-full-body-layout--controls .memori-chat--bubble.focused:active, + .memori-full-body-layout--controls .memori-chat--bubble.memori-chat--user-bubble.focused:active { + background-color: #fff; + opacity: 1; + } + } + + /* Hover states for non-touch devices */ + @media (hover: hover) { + .memori-full-body-layout--controls .memori-chat--bubble:hover, + .memori-full-body-layout--controls .memori-chat--bubble.memori-chat--user-bubble:hover { + opacity: 1; + } + + /* Maintain focus styles on hover */ + .memori-full-body-layout--controls .memori-chat--bubble.focused:hover, + .memori-full-body-layout--controls .memori-chat--bubble.memori-chat--user-bubble.focused:hover { + background-color: #fff; + opacity: 1; + } + } + + /* Safe area insets for modern mobile devices */ + @supports (padding: max(0px)) { + .memori-full-body-layout--controls { + padding-bottom: max(1rem, env(safe-area-inset-bottom)); + } + + .memori-header { + padding-top: max(1rem, env(safe-area-inset-top)); + } + } +} + +/* Momentum scrolling for iOS devices */ +@supports (-webkit-overflow-scrolling: touch) { + .memori-scrollable { + -webkit-overflow-scrolling: touch; + } +} diff --git a/src/components/ui/Spin.css b/src/components/ui/Spin.css index 9dbdc270..ba55980e 100644 --- a/src/components/ui/Spin.css +++ b/src/components/ui/Spin.css @@ -29,3 +29,23 @@ .memori-spin--primary svg { fill: var(--memori-primary); } + + +@media (max-width: 870px) { + + .memori-spin--spinning { + top: 60% !important; + left: 110%; + font-size: 1rem; + } + + .memori-spin--spinner { + margin: auto; + background: transparent ; + } + + .memori-spin--spinner svg { + width: 20%; + height: 20%; + } +} \ No newline at end of file