Skip to content

Commit 64ae009

Browse files
authored
Merge pull request #214 from ayoub3bidi/fix/landing-page-sign-in-flash
fix: mobile responsiveness and sign-in flash prevention
2 parents 60809e4 + 90fad94 commit 64ae009

8 files changed

Lines changed: 375 additions & 68 deletions

src/components/ComplexityPanel.jsx

Lines changed: 93 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* See LICENSE for details.
55
*/
66

7-
import { useState, useRef, useEffect } from 'react';
7+
import { useState, useRef, useEffect, useCallback } from 'react';
88
import { motion, useReducedMotion } from 'framer-motion';
99
import { useTranslation } from 'react-i18next';
1010
import { COMPLEXITY_FUNCTIONS } from '../constants';
@@ -17,6 +17,11 @@ import {
1717
COMPLEXITY_DATASETS,
1818
DEFAULT_COMPLEXITY_DATASET,
1919
} from '../registry/complexityDatasetRegistry';
20+
import { useMediaQuery } from '../hooks/useMediaQuery';
21+
22+
// Fixed internal viewBox — CSS scales the SVG to its container
23+
const VIEWBOX_WIDTH = 650;
24+
const VIEWBOX_HEIGHT = 350;
2025

2126
/**
2227
* @param {string} algorithm - Current algorithm key
@@ -32,11 +37,24 @@ function ComplexityPanel({
3237
const [hoveredPoint, setHoveredPoint] = useState(null);
3338
const [animationProgress, setAnimationProgress] = useState(0);
3439
const svgRef = useRef(null);
40+
41+
const isSmall = useMediaQuery('(max-width: 639px)');
42+
const isBelowLg = useMediaQuery('(max-width: 1023px)');
43+
3544
const dataset =
3645
COMPLEXITY_DATASETS[complexityDataset] ??
3746
COMPLEXITY_DATASETS[DEFAULT_COMPLEXITY_DATASET];
3847
const complexityData = dataset[algorithm];
3948

49+
// Touch-friendly tooltip: tap toggles, tap elsewhere dismisses
50+
const handlePointInteraction = useCallback(point => {
51+
setHoveredPoint(prev => (prev && prev.n === point.n ? null : point));
52+
}, []);
53+
54+
const dismissTooltip = useCallback(() => {
55+
setHoveredPoint(null);
56+
}, []);
57+
4058
// Animate curve drawing on mount
4159
useEffect(() => {
4260
setAnimationProgress(0);
@@ -74,13 +92,14 @@ function ComplexityPanel({
7492

7593
const graphData = generateGraphData();
7694

77-
// SVG dimensions - responsive for mobile
78-
const isMobile = typeof window !== 'undefined' && window.innerWidth < 640;
79-
const width = isMobile ? 320 : 650;
80-
const height = isMobile ? 280 : 350;
81-
const margin = { top: 30, right: 50, bottom: 60, left: 60 };
82-
const chartWidth = width - margin.left - margin.right;
83-
const chartHeight = height - margin.top - margin.bottom;
95+
// Responsive margins for the SVG viewBox coordinate system
96+
const margin = isSmall
97+
? { top: 20, right: 25, bottom: 50, left: 50 }
98+
: isBelowLg
99+
? { top: 25, right: 35, bottom: 55, left: 55 }
100+
: { top: 30, right: 50, bottom: 60, left: 60 };
101+
const chartWidth = VIEWBOX_WIDTH - margin.left - margin.right;
102+
const chartHeight = VIEWBOX_HEIGHT - margin.top - margin.bottom;
84103

85104
// Scales
86105
const maxN = Math.max(...graphData.map(d => d.n));
@@ -109,6 +128,12 @@ function ComplexityPanel({
109128
.join(' ');
110129
};
111130

131+
const svgTextClass = isSmall
132+
? 'text-[8px]'
133+
: isBelowLg
134+
? 'text-[9px]'
135+
: 'text-xs';
136+
112137
return (
113138
<motion.div
114139
initial={modalPanelInitial(reduceMotion)}
@@ -118,68 +143,79 @@ function ComplexityPanel({
118143
dir="auto"
119144
>
120145
<div className="rounded-xl p-3 sm:p-6 max-w-5xl w-full">
121-
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 mb-4 pb-3 border-b border-gray-200">
122-
<div className="hidden sm:block">
123-
<h2 className="text-lg sm:text-xl font-bold text-text-primary">
146+
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2 sm:gap-3 mb-3 sm:mb-4 pb-3 border-b border-gray-200">
147+
<div>
148+
<h2 className="text-sm sm:text-xl font-bold text-text-primary">
124149
{t('complexity_panel.title')}
125150
</h2>
126-
<p className="text-xs sm:text-sm text-text-secondary">
151+
<p className="text-[10px] sm:text-sm text-text-secondary">
127152
{t(`algorithms.${complexityDataset}.${algorithm}`, {
128153
defaultValue: complexityData.name || algorithm,
129154
})}
130155
</p>
131156
</div>
132-
<div className="flex items-center gap-2 sm:gap-3">
133-
<span className="text-xs sm:text-sm text-text-secondary">
157+
<div className="hidden sm:flex items-center gap-3">
158+
<span className="hidden sm:inline text-sm text-text-secondary">
134159
{t('complexity_panel.linearScale')}
135160
</span>
136161
<button
137162
onClick={() => setIsLogScale(!isLogScale)}
138-
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 touch-manipulation ${
163+
className={`relative inline-flex h-7 w-12 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 touch-manipulation ${
139164
isLogScale ? 'bg-blue-600' : 'bg-gray-300'
140165
}`}
141166
role="switch"
142167
aria-checked={isLogScale}
143168
aria-label={t('complexity_panel.toggleScale')}
144169
>
145170
<span
146-
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
171+
className={`inline-block h-5 w-5 transform rounded-full bg-white shadow-sm transition-transform ${
147172
isLogScale
148173
? 'translate-x-6 rtl:-translate-x-6'
149174
: 'translate-x-1 rtl:-translate-x-1'
150175
}`}
151176
/>
152177
</button>
153-
<span className="text-xs sm:text-sm text-text-secondary">
178+
<span className="hidden sm:inline text-sm text-text-secondary">
154179
{t('complexity_panel.logScale')}
155180
</span>
156181
</div>
157182
</div>
158-
<div className="flex flex-col lg:flex-row gap-4 sm:gap-6">
159-
<div className="space-y-3 sm:space-y-4 flex-shrink-0 w-full lg:w-auto">
160-
<div>
161-
<h3 className="text-[10px] sm:text-xs font-semibold text-text-secondary mb-2 uppercase tracking-wide">
183+
<div className="flex flex-col lg:flex-row gap-3 sm:gap-6">
184+
{/* Complexity badges */}
185+
<div
186+
className={`flex-shrink-0 w-full lg:w-auto ${
187+
isSmall
188+
? 'flex flex-row flex-wrap gap-x-4 gap-y-2'
189+
: 'space-y-3 sm:space-y-4'
190+
}`}
191+
>
192+
<div className={isSmall ? 'min-w-0' : ''}>
193+
<h3 className="text-[10px] sm:text-xs font-semibold text-text-secondary mb-1.5 sm:mb-2 uppercase tracking-wide">
162194
{t('complexity_panel.timeComplexity')}
163195
</h3>
164-
<div className="space-y-1.5">
165-
<div className="flex items-center gap-2">
166-
<span className="text-[10px] sm:text-xs text-text-green-500 w-12 sm:w-16 rtl:text-right">
196+
<div
197+
className={
198+
isSmall ? 'flex flex-wrap gap-x-3 gap-y-1' : 'space-y-1.5'
199+
}
200+
>
201+
<div className="flex items-center gap-1.5 sm:gap-2">
202+
<span className="text-[10px] sm:text-xs text-text-green-500 w-10 sm:w-16 rtl:text-right">
167203
{t('complexity_panel.best')}:
168204
</span>
169205
<code className="bg-green-100 text-green-800 px-1.5 sm:px-2 py-0.5 sm:py-1 rounded font-mono text-[10px] sm:text-xs font-semibold">
170206
{complexityData.timeComplexity.best}
171207
</code>
172208
</div>
173-
<div className="flex items-center gap-2">
174-
<span className="text-[10px] sm:text-xs text-text-tertiary w-12 sm:w-16 rtl:text-right">
209+
<div className="flex items-center gap-1.5 sm:gap-2">
210+
<span className="text-[10px] sm:text-xs text-text-tertiary w-10 sm:w-16 rtl:text-right">
175211
{t('complexity_panel.average')}:
176212
</span>
177213
<code className="bg-blue-100 text-blue-800 px-1.5 sm:px-2 py-0.5 sm:py-1 rounded font-mono text-[10px] sm:text-xs font-semibold">
178214
{complexityData.timeComplexity.average}
179215
</code>
180216
</div>
181-
<div className="flex items-center gap-2">
182-
<span className="text-[10px] sm:text-xs text-text-tertiary w-12 sm:w-16 rtl:text-right">
217+
<div className="flex items-center gap-1.5 sm:gap-2">
218+
<span className="text-[10px] sm:text-xs text-text-tertiary w-10 sm:w-16 rtl:text-right">
183219
{t('complexity_panel.worst')}:
184220
</span>
185221
<code className="bg-red-100 text-red-800 px-1.5 sm:px-2 py-0.5 sm:py-1 rounded font-mono text-[10px] sm:text-xs font-semibold">
@@ -188,31 +224,33 @@ function ComplexityPanel({
188224
</div>
189225
</div>
190226
</div>
191-
<div>
192-
<h3 className="text-[10px] sm:text-xs font-semibold text-text-secondary mb-2 uppercase tracking-wide">
227+
<div className={isSmall ? 'min-w-0' : ''}>
228+
<h3 className="text-[10px] sm:text-xs font-semibold text-text-secondary mb-1.5 sm:mb-2 uppercase tracking-wide">
193229
{t('complexity_panel.spaceComplexity')}
194230
</h3>
195231
<code className="bg-purple-100 text-purple-800 px-1.5 sm:px-2 py-0.5 sm:py-1 rounded font-mono text-[10px] sm:text-xs font-semibold inline-block">
196232
{complexityData.spaceComplexity}
197233
</code>
198234
</div>
199235
</div>
200-
<div className="flex-1 w-full overflow-x-auto">
201-
<h3 className="text-xs sm:text-sm font-semibold text-text-primary mb-2">
236+
237+
{/* Chart */}
238+
<div className="flex-1 w-full min-w-0">
239+
<h3 className="text-xs sm:text-sm font-semibold text-text-primary mb-1 sm:mb-2">
202240
{t('complexity_panel.performance')}
203241
</h3>
204-
<div className="text-[10px] sm:text-xs text-text-secondary mb-3">
242+
<div className="text-[10px] sm:text-xs text-text-secondary mb-2 sm:mb-3">
205243
{t('complexity_panel.axisLabels', {
206244
complexity: complexityData.timeComplexity.average,
207245
})}
208246
</div>
209247

210-
<div className="relative w-full overflow-x-auto">
248+
<div className="relative w-full" onClick={dismissTooltip}>
211249
<svg
212250
ref={svgRef}
213-
width={width}
214-
height={height}
215-
className="border border-gray-200 rounded bg-bg w-full"
251+
viewBox={`0 0 ${VIEWBOX_WIDTH} ${VIEWBOX_HEIGHT}`}
252+
className="border border-gray-200 rounded bg-bg w-full h-auto"
253+
preserveAspectRatio="xMidYMid meet"
216254
>
217255
<g transform={`translate(${margin.left}, ${margin.top})`}>
218256
<defs>
@@ -276,23 +314,27 @@ function ComplexityPanel({
276314
className="cursor-pointer transition-all duration-200"
277315
onMouseEnter={() => setHoveredPoint(point)}
278316
onMouseLeave={() => setHoveredPoint(null)}
317+
onClick={e => {
318+
e.stopPropagation();
319+
handlePointInteraction(point);
320+
}}
279321
/>
280322
))}
281323
<text
282324
x={chartWidth / 2}
283-
y={chartHeight + 35}
325+
y={chartHeight + (isSmall ? 30 : 35)}
284326
textAnchor="middle"
285-
className="text-xs font-medium"
327+
className={`${svgTextClass} font-medium`}
286328
fill="var(--color-text-secondary)"
287329
>
288330
{t('complexity_panel.inputSize')}
289331
</text>
290332
<text
291333
x={-chartHeight / 2}
292-
y={-45}
334+
y={isSmall ? -38 : -45}
293335
textAnchor="middle"
294-
transform={`rotate(-90, -45, ${chartHeight / 2})`}
295-
className="text-xs font-medium"
336+
transform={`rotate(-90, ${isSmall ? -38 : -45}, ${chartHeight / 2})`}
337+
className={`${svgTextClass} font-medium`}
296338
fill="var(--color-text-secondary)"
297339
>
298340
{t('complexity_panel.operations', {
@@ -305,7 +347,7 @@ function ComplexityPanel({
305347
x="0"
306348
y={chartHeight + 15}
307349
textAnchor="start"
308-
className="text-xs"
350+
className={svgTextClass}
309351
fill="var(--color-text-tertiary)"
310352
>
311353
0
@@ -314,7 +356,7 @@ function ComplexityPanel({
314356
x={chartWidth}
315357
y={chartHeight + 15}
316358
textAnchor="end"
317-
className="text-xs"
359+
className={svgTextClass}
318360
fill="var(--color-text-tertiary)"
319361
>
320362
{maxN.toLocaleString()}
@@ -323,7 +365,7 @@ function ComplexityPanel({
323365
x="-10"
324366
y={chartHeight + 4}
325367
textAnchor="end"
326-
className="text-xs"
368+
className={svgTextClass}
327369
fill="var(--color-text-tertiary)"
328370
>
329371
0
@@ -332,7 +374,7 @@ function ComplexityPanel({
332374
x="-10"
333375
y="4"
334376
textAnchor="end"
335-
className="text-xs"
377+
className={svgTextClass}
336378
fill="var(--color-text-tertiary)"
337379
>
338380
{isLogScale
@@ -342,7 +384,11 @@ function ComplexityPanel({
342384
</g>
343385
</svg>
344386
{hoveredPoint && (
345-
<div className="absolute top-2 ltr:left-2 rtl:right-2 bg-surface-elevated border border-gray-200 text-text-primary text-xs px-3 py-2 rounded shadow-lg z-10">
387+
<div
388+
className="absolute top-2 ltr:left-2 rtl:right-2 bg-surface-elevated border border-gray-200 text-text-primary text-[10px] sm:text-xs px-2 sm:px-3 py-1.5 sm:py-2 rounded shadow-lg z-10"
389+
role="status"
390+
aria-live="polite"
391+
>
346392
<div>
347393
{t('complexity_panel.hoverN')} = {hoveredPoint.n}
348394
</div>

0 commit comments

Comments
 (0)