From 444400bddf8e9dfadee1270da012dc64c79e8999 Mon Sep 17 00:00:00 2001 From: Constantin Rusu Date: Tue, 28 Jan 2025 00:45:38 +0000 Subject: [PATCH] fix dossier and shadows for scrollable dialogues --- src/components/game/DossierPanel.tsx | 19 +++--- src/components/game/EndGameDialog.tsx | 20 ++++--- src/components/game/ExpertMemo.css | 49 ++++++++++++++- src/components/game/ExpertMemo.tsx | 31 +++++++++- src/components/game/IntroDialog.tsx | 86 ++++++++++++++------------- src/components/ui/dialog.tsx | 41 +++++++------ 6 files changed, 166 insertions(+), 80 deletions(-) diff --git a/src/components/game/DossierPanel.tsx b/src/components/game/DossierPanel.tsx index f9f3340..dd40248 100644 --- a/src/components/game/DossierPanel.tsx +++ b/src/components/game/DossierPanel.tsx @@ -54,8 +54,8 @@ export const DossierPanel = ({ entries, choices = [] }: DossierPanelProps) => { {t('dossier.button')} - - + + {t('dossier.clearanceRequired')} @@ -64,13 +64,14 @@ export const DossierPanel = ({ entries, choices = [] }: DossierPanelProps) => { -
- -
+ +
+
+ +
- - -
+ + {entries.length === 0 ? (

{t('dossier.noIntelligence')}

) : ( @@ -80,7 +81,7 @@ export const DossierPanel = ({ entries, choices = [] }: DossierPanelProps) => { initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5, delay: index * 0.1 }} - className="space-y-4 relative bg-gray-800/30 p-6 rounded-md border border-gray-700" + className="space-y-4 relative bg-gray-800/30 p-4 sm:p-6 rounded-md border border-gray-700" >

diff --git a/src/components/game/EndGameDialog.tsx b/src/components/game/EndGameDialog.tsx index 7675570..29581d0 100644 --- a/src/components/game/EndGameDialog.tsx +++ b/src/components/game/EndGameDialog.tsx @@ -66,19 +66,21 @@ export const EndGameDialog = ({ onContinue, startFade }: EndGameDialogProps) => return ( - - button]:hidden", - "z-[50] fixed left-[50%] top-[50%] grid w-full translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg" - )} - onPointerDownOutside={(e) => e.preventDefault()} - onEscapeKeyDown={(e) => e.preventDefault()} - > + + diff --git a/src/components/game/ExpertMemo.css b/src/components/game/ExpertMemo.css index 38d8488..eb8d2f8 100644 --- a/src/components/game/ExpertMemo.css +++ b/src/components/game/ExpertMemo.css @@ -21,9 +21,12 @@ inset 0 0 60px rgba(0, 0, 0, 0.6); width: 100%; max-width: 800px; + max-height: 80vh; margin: 0 auto; position: relative; color: #e8e8e8; + display: flex; + flex-direction: column; } @media (min-width: 640px) { @@ -92,12 +95,56 @@ white-space: pre-wrap; line-height: 1.5; text-shadow: 0 0 1px rgba(255, 255, 255, 0.1); + overflow-y: auto; + flex: 1; + padding-right: 0.5rem; + position: relative; + -webkit-overflow-scrolling: touch; + max-height: calc(80vh - 12rem); } +/* Gradient container */ +.memo-gradient { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 60px; + background: linear-gradient(to top, #1a1715 10%, rgba(26, 23, 21, 0.8) 40%, transparent 100%); + pointer-events: none; + opacity: 0; + transition: opacity 0.3s ease; + z-index: 10; +} + +.memo-gradient.show { + opacity: 0.95; +} + +/* Custom scrollbar for WebKit browsers */ +.memo-body::-webkit-scrollbar { + width: 8px; +} + +.memo-body::-webkit-scrollbar-track { + background: transparent; +} + +.memo-body::-webkit-scrollbar-thumb { + background-color: rgba(255, 255, 255, 0.2); + border-radius: 4px; +} + +/* Show scrollbar on hover */ +.memo-body:hover::-webkit-scrollbar-thumb { + background-color: rgba(255, 255, 255, 0.3); +} + +/* Remove the old gradient and padding styles */ .memo-body p { margin-bottom: 1.5rem; } .memo-body p:last-child { - margin-bottom: 0; + margin-bottom: 1rem; } \ No newline at end of file diff --git a/src/components/game/ExpertMemo.tsx b/src/components/game/ExpertMemo.tsx index 3a230f3..e11d8ae 100644 --- a/src/components/game/ExpertMemo.tsx +++ b/src/components/game/ExpertMemo.tsx @@ -1,7 +1,8 @@ -import React from 'react'; +import React, { useEffect, useState, useRef, useCallback } from 'react'; import './ExpertMemo.css'; import { useTranslation } from 'react-i18next'; import { BriefingAudio } from './BriefingAudio'; +import { cn } from '@/lib/utils'; interface ExpertMemoProps { from: string; @@ -16,6 +17,31 @@ export const ExpertMemo: React.FC = ({ from, subject, children, const { t } = useTranslation(); const highlightColor = isAlert ? 'text-red-500' : 'text-yellow-500'; const memoClass = isAlert ? 'expert-memo alert' : 'expert-memo'; + const [showGradient, setShowGradient] = useState(false); + const memoBodyRef = useRef(null); + + const checkScroll = useCallback(() => { + const element = memoBodyRef.current; + if (element) { + const hasOverflow = element.scrollHeight > element.clientHeight; + const isAtBottom = Math.abs(element.scrollHeight - element.clientHeight - element.scrollTop) < 1; + setShowGradient(hasOverflow && !isAtBottom); + } + }, []); + + useEffect(() => { + const element = memoBodyRef.current; + if (element) { + checkScroll(); + element.addEventListener('scroll', checkScroll); + window.addEventListener('resize', checkScroll); + + return () => { + element.removeEventListener('scroll', checkScroll); + window.removeEventListener('resize', checkScroll); + }; + } + }, [checkScroll]); // Function to wrap text content in paragraph tags const formatContent = (content: React.ReactNode) => { @@ -53,9 +79,10 @@ export const ExpertMemo: React.FC = ({ from, subject, children,

)}
-
+
{formatContent(children)}
+
); }; \ No newline at end of file diff --git a/src/components/game/IntroDialog.tsx b/src/components/game/IntroDialog.tsx index c011dd5..c11f113 100644 --- a/src/components/game/IntroDialog.tsx +++ b/src/components/game/IntroDialog.tsx @@ -24,48 +24,54 @@ export const IntroDialog = ({ onStartAudio }: IntroDialogProps) => { return ( - - - - {t('intro.title')} - - -
-
-
🎯
-

- {t('intro.mission')} -

-
- -

- {t('intro.explanation')} -

- -

- {t('intro.howToPlay.description')} -

- -

- {t('intro.reminder')} -

-
-
+ +
+
+ + + {t('intro.title')} + + +
+
+
🎯
+

+ {t('intro.mission')} +

+
+ +

+ {t('intro.explanation')} +

+ +

+ {t('intro.howToPlay.description')} +

+ +

+ {t('intro.reminder')} +

+
+
-
-
- - - {t('languageSwitcher.hint')} - +
+
+ + + {t('languageSwitcher.hint')} + +
+ + +
- -
diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx index 04f4350..4a51553 100644 --- a/src/components/ui/dialog.tsx +++ b/src/components/ui/dialog.tsx @@ -32,25 +32,28 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName const DialogContent = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - - {children} - - - Close - - - -)) +>(({ className, children, ...props }, ref) => { + return ( + + + + {children} + + + Close + + + + ); +}) DialogContent.displayName = DialogPrimitive.Content.displayName const DialogHeader = ({