зеркало из
https://github.com/kodackx/disinformation-quest.git
synced 2025-10-29 12:46:03 +02:00
fix dossier and shadows for scrollable dialogues
Этот коммит содержится в:
родитель
a457733ce9
Коммит
444400bddf
@ -54,8 +54,8 @@ export const DossierPanel = ({ entries, choices = [] }: DossierPanelProps) => {
|
||||
{t('dossier.button')}
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent className="w-[95vw] sm:w-[90vw] lg:w-[45vw] bg-[#1a1a1a] border-gray-700 text-white overflow-hidden p-8 pt-10 !max-w-[100vw]">
|
||||
<SheetHeader className="mb-6">
|
||||
<SheetContent className="w-[95vw] sm:w-[90vw] lg:w-[45vw] bg-[#1a1a1a] border-gray-700 text-white overflow-hidden p-2 sm:p-8 pt-10 !max-w-[100vw] flex flex-col">
|
||||
<SheetHeader className="mb-6 flex-none">
|
||||
<SheetTitle className="text-yellow-500 relative">
|
||||
<span className="absolute -top-6 left-0 text-xs text-red-500 tracking-wider font-mono">
|
||||
{t('dossier.clearanceRequired')}
|
||||
@ -64,13 +64,14 @@ export const DossierPanel = ({ entries, choices = [] }: DossierPanelProps) => {
|
||||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
|
||||
<div className="bg-gray-800/30 p-6 rounded-md border border-gray-700 mb-6">
|
||||
<ScrollArea className="flex-1 min-h-0">
|
||||
<div className="space-y-6 pb-4 px-2 sm:px-4">
|
||||
<div className="bg-gray-800/30 p-4 sm:p-6 rounded-md border border-gray-700">
|
||||
<MetricsDisplay choices={choices} className="pl-0" />
|
||||
</div>
|
||||
|
||||
<Separator className="my-6 bg-gray-700" />
|
||||
<ScrollArea className="h-[calc(100vh-320px)] pr-4">
|
||||
<div className="space-y-6 pb-16">
|
||||
<Separator className="bg-gray-700" />
|
||||
|
||||
{entries.length === 0 ? (
|
||||
<p className="text-gray-400 italic">{t('dossier.noIntelligence')}</p>
|
||||
) : (
|
||||
@ -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"
|
||||
>
|
||||
<div>
|
||||
<h3 className="text-yellow-500 font-semibold flex items-center gap-3">
|
||||
|
||||
@ -66,19 +66,21 @@ export const EndGameDialog = ({ onContinue, startFade }: EndGameDialogProps) =>
|
||||
return (
|
||||
<Dialog open={open}>
|
||||
<DialogPortal>
|
||||
<DialogOverlay className="z-[45]" />
|
||||
<DialogContent
|
||||
className={cn(
|
||||
"bg-black/95 text-white border-emerald-900/50 max-w-2xl [&>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()}
|
||||
>
|
||||
<DialogOverlay className={cn(
|
||||
"bg-black/95 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
startFade && "animate-fade-out"
|
||||
)} />
|
||||
<DialogContent className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-emerald-900/50 bg-black/95 p-6 shadow-lg duration-200 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=open]:slide-in-from-left-1/2 sm:rounded-lg",
|
||||
startFade && "animate-fade-out",
|
||||
"max-h-[90vh] overflow-y-auto scrollbar-thin scrollbar-thumb-emerald-500/20 scrollbar-track-transparent",
|
||||
"relative after:absolute after:bottom-0 after:left-0 after:right-0 after:h-16 after:bg-gradient-to-t after:from-black/95 after:to-transparent after:pointer-events-none after:z-50"
|
||||
)}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.8, ease: "easeOut" }}
|
||||
className="relative z-10 pb-16"
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-emerald-500 text-2xl mb-6">
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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<ExpertMemoProps> = ({ 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<HTMLDivElement>(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<ExpertMemoProps> = ({ from, subject, children,
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="memo-body text-gray-300">
|
||||
<div ref={memoBodyRef} className="memo-body">
|
||||
{formatContent(children)}
|
||||
</div>
|
||||
<div className={cn("memo-gradient", showGradient && "show")} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -24,7 +24,11 @@ export const IntroDialog = ({ onStartAudio }: IntroDialogProps) => {
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent className="bg-black/90 text-white border-gray-700 max-w-2xl">
|
||||
<DialogContent
|
||||
className="bg-black text-white border-gray-700 max-w-2xl max-h-[85vh] overflow-y-auto"
|
||||
>
|
||||
<div className="relative flex-1 min-h-0">
|
||||
<div className="space-y-6">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-yellow-500 text-2xl mb-6">
|
||||
{t('intro.title')}
|
||||
@ -67,6 +71,8 @@ export const IntroDialog = ({ onStartAudio }: IntroDialogProps) => {
|
||||
{t('buttons.beginSimulation')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@ -32,13 +32,15 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
|
||||
const DialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
>(({ className, children, ...props }, ref) => {
|
||||
return (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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",
|
||||
"fixed left-[50%] top-[50%] z-50 flex flex-col w-full max-w-lg translate-x-[-50%] translate-y-[-50%] border bg-background p-6 shadow-lg duration-200 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=open]:slide-in-from-left-1/2 sm:rounded-lg",
|
||||
"max-h-[85vh] overflow-y-auto scrollbar-thin scrollbar-thumb-gray-400 scrollbar-track-transparent",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@ -50,7 +52,8 @@ const DialogContent = React.forwardRef<
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
))
|
||||
);
|
||||
})
|
||||
DialogContent.displayName = DialogPrimitive.Content.displayName
|
||||
|
||||
const DialogHeader = ({
|
||||
|
||||
Загрузка…
x
Ссылка в новой задаче
Block a user