Fix missing CSS for page-title-with-actions and period-display-banner

Added CSS rules that were missing after restoring from older commit:
- .page-title-with-actions (flex layout for title + toggle)
- .toggle-with-label (Labels On/Off toggle styling)
- .period-display-banner (Comparison page period display)
This commit is contained in:
fahed
2026-02-03 15:18:26 +03:00
parent 480885a8e6
commit 97afb8b3a7
2 changed files with 94 additions and 312 deletions

View File

@@ -325,6 +325,37 @@ body {
font-size: 0.875rem; font-size: 0.875rem;
} }
/* Page Title with Actions (Labels toggle) */
.page-title-with-actions {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 24px;
}
.page-title-with-actions .page-title {
margin-bottom: 0;
}
.page-title-with-actions .toggle-with-label {
display: flex;
align-items: center;
gap: 6px;
margin-top: 4px;
}
.toggle-with-label .toggle-text {
font-size: 0.6875rem;
font-weight: 500;
color: var(--text-muted);
}
.page-title-with-actions .toggle-switch button {
padding: 3px 8px;
font-size: 0.6875rem;
}
/* Filters - now uses .controls for consistency */ /* Filters - now uses .controls for consistency */
/* Stats Grid */ /* Stats Grid */
@@ -599,6 +630,58 @@ table tbody tr:hover {
font-weight: 500; font-weight: 500;
} }
/* Period Display Banner */
.period-display-banner {
display: flex;
align-items: center;
justify-content: center;
gap: 24px;
background: var(--bg);
padding: 20px 32px;
border-radius: 12px;
margin-bottom: 24px;
}
.period-display-banner .period-box {
text-align: center;
min-width: 180px;
}
.period-display-banner .period-box.prev {
color: var(--text-secondary);
}
.period-display-banner .period-box.curr {
color: var(--text-primary);
}
.period-display-banner .period-label {
font-size: 0.6875rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-muted);
margin-bottom: 4px;
}
.period-display-banner .period-value {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 2px;
}
.period-display-banner .period-dates {
font-size: 0.8125rem;
color: var(--text-muted);
}
.period-display-banner .period-vs {
font-size: 0.875rem;
font-weight: 500;
color: var(--text-muted);
padding: 0 8px;
}
/* Comparison Metrics */ /* Comparison Metrics */
.comparison-grid { .comparison-grid {
display: grid; display: grid;
@@ -1812,244 +1895,3 @@ table tbody tr:hover {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
/* ========================================
MOBILE EXCELLENCE ENHANCEMENTS
These rules ONLY apply to mobile (≤768px)
and do not affect desktop layout
======================================== */
@media (max-width: 768px) {
/* Enhanced touch targets - minimum 44px */
.mobile-nav-item {
min-height: 44px;
min-width: 60px;
}
.carousel-dot {
min-height: 36px;
min-width: 36px;
}
.toggle-switch button {
min-height: 32px;
padding: 6px 12px;
}
.chart-metric-selector button {
min-height: 28px;
padding: 4px 8px;
}
/* Smoother carousel transitions */
.carousel-track {
transition: transform 350ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
will-change: transform;
}
/* Better visual feedback on touch */
.carousel-dot:active,
.mobile-nav-item:active,
.toggle-switch button:active,
.stat-card:active {
transform: scale(0.96);
transition: transform 100ms ease;
}
/* Active indicator for bottom nav */
.mobile-nav-item.active {
position: relative;
}
.mobile-nav-item.active::after {
content: '';
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 20px;
height: 3px;
background: var(--primary);
border-radius: 0 0 3px 3px;
}
/* Improved stat cards in carousel */
.stats-carousel .stat-card {
min-height: 100px;
display: flex;
flex-direction: column;
justify-content: center;
}
/* Better chart card spacing */
.charts-carousel .chart-card {
padding: 16px;
}
.charts-carousel .chart-card h2 {
font-size: 0.875rem;
margin-bottom: 12px;
}
/* Ensure toggle corner doesn't overlap title */
.charts-carousel .chart-card h2 {
padding-right: 90px;
}
.charts-carousel .toggle-corner {
top: 12px;
right: 12px;
}
/* Improved carousel dot labels */
.carousel-dots.labeled .carousel-dot {
padding: 6px 12px;
border-radius: 16px;
}
.carousel-dots.labeled .carousel-dot.active {
background: var(--primary);
border-color: var(--primary);
}
.carousel-dots.labeled .carousel-dot.active .dot-label {
color: white;
}
/* Filters - more compact on mobile */
.controls {
padding: 12px 14px;
margin-bottom: 16px;
}
.controls h3 {
font-size: 0.6875rem;
}
.control-group label {
font-size: 0.625rem;
}
.control-group select,
.control-group input[type="date"] {
font-size: 0.8125rem;
padding: 10px 12px;
min-height: 44px;
}
/* Period banner - stacked on mobile */
.period-display-banner {
flex-direction: column;
gap: 16px;
padding: 16px;
}
.period-display-banner .period-box {
min-width: unset;
}
.period-display-banner .period-value {
font-size: 1.25rem;
}
.period-display-banner .period-dates {
font-size: 0.75rem;
}
/* Table scroll hint - fade at edges */
.table-container {
position: relative;
}
.table-container::after {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 20px;
background: linear-gradient(to left, var(--surface), transparent);
pointer-events: none;
}
/* Page title adjustments */
.page-title {
margin-bottom: 16px;
}
.page-title h1 {
font-size: 1.25rem;
line-height: 1.3;
}
.page-title p {
font-size: 0.8125rem;
line-height: 1.4;
}
/* Compact header toggle */
.page-title-with-actions .toggle-switch button {
padding: 4px 8px;
font-size: 0.625rem;
min-height: 26px;
}
.toggle-with-label .toggle-text {
font-size: 0.625rem;
}
}
/* Extra small screens (≤375px) */
@media (max-width: 375px) {
.dashboard,
.comparison {
padding: 12px;
padding-bottom: 80px;
}
.page-title h1 {
font-size: 1.125rem;
}
.stats-carousel .stat-value {
font-size: 1.5rem;
}
.charts-carousel .chart-container {
height: 200px;
}
.carousel-dot {
min-height: 32px;
min-width: 32px;
padding: 4px 8px;
}
.carousel-dot .dot-label {
font-size: 0.5625rem;
}
.mobile-nav-item {
padding: 6px 10px;
font-size: 0.5625rem;
}
.mobile-nav-item svg {
width: 20px;
height: 20px;
}
/* Smaller table text */
table {
font-size: 0.6875rem;
}
table th {
font-size: 0.5625rem;
padding: 8px 4px;
}
table td {
padding: 8px 4px;
}
}

View File

@@ -1,4 +1,4 @@
import React, { useRef, useCallback, useState } from 'react'; import React, { useRef, useCallback } from 'react';
function Carousel({ function Carousel({
children, children,
@@ -8,62 +8,25 @@ function Carousel({
showLabels = true, showLabels = true,
className = '' className = ''
}) { }) {
const touchStartX = useRef(null); const touchStart = useRef(null);
const touchStartY = useRef(null);
const trackRef = useRef(null);
const [isDragging, setIsDragging] = useState(false);
const [dragOffset, setDragOffset] = useState(0);
const itemCount = React.Children.count(children); const itemCount = React.Children.count(children);
const SWIPE_THRESHOLD = 50;
const handleTouchStart = useCallback((e) => { const handleTouchStart = useCallback((e) => {
touchStartX.current = e.touches[0].clientX; touchStart.current = e.touches[0].clientX;
touchStartY.current = e.touches[0].clientY;
setIsDragging(true);
setDragOffset(0);
}, []); }, []);
const handleTouchMove = useCallback((e) => {
if (!touchStartX.current || !isDragging) return;
const currentX = e.touches[0].clientX;
const currentY = e.touches[0].clientY;
const diffX = currentX - touchStartX.current;
const diffY = Math.abs(currentY - touchStartY.current);
// Only handle horizontal swipes (prevent vertical scroll interference)
if (Math.abs(diffX) > diffY) {
// Add resistance at edges (rubber band effect)
let offset = diffX;
if ((activeIndex === 0 && diffX > 0) || (activeIndex === itemCount - 1 && diffX < 0)) {
offset = diffX * 0.25;
}
setDragOffset(offset);
}
}, [isDragging, activeIndex, itemCount]);
const handleTouchEnd = useCallback((e) => { const handleTouchEnd = useCallback((e) => {
if (!touchStartX.current || !isDragging) return; if (!touchStart.current) return;
const diff = touchStart.current - e.changedTouches[0].clientX;
const endX = e.changedTouches[0].clientX; if (Math.abs(diff) > 50) {
const diff = touchStartX.current - endX;
// Change slide if swipe exceeds threshold
if (Math.abs(diff) > SWIPE_THRESHOLD) {
if (diff > 0 && activeIndex < itemCount - 1) { if (diff > 0 && activeIndex < itemCount - 1) {
setActiveIndex(activeIndex + 1); setActiveIndex(activeIndex + 1);
} else if (diff < 0 && activeIndex > 0) { } else if (diff < 0 && activeIndex > 0) {
setActiveIndex(activeIndex - 1); setActiveIndex(activeIndex - 1);
} }
} }
touchStart.current = null;
// Reset }, [activeIndex, setActiveIndex, itemCount]);
touchStartX.current = null;
touchStartY.current = null;
setIsDragging(false);
setDragOffset(0);
}, [isDragging, activeIndex, setActiveIndex, itemCount]);
const handleKeyDown = useCallback((e) => { const handleKeyDown = useCallback((e) => {
if (e.key === 'ArrowLeft' && activeIndex > 0) { if (e.key === 'ArrowLeft' && activeIndex > 0) {
@@ -73,41 +36,18 @@ function Carousel({
} }
}, [activeIndex, setActiveIndex, itemCount]); }, [activeIndex, setActiveIndex, itemCount]);
// Calculate transform with drag offset
const baseTransform = -(activeIndex * 100);
const dragPercentage = trackRef.current
? (dragOffset / trackRef.current.offsetWidth) * 100
: 0;
const transform = baseTransform + dragPercentage;
return ( return (
<div <div className={`carousel ${className}`} onKeyDown={handleKeyDown} tabIndex={0}>
className={`carousel ${className}`}
onKeyDown={handleKeyDown}
tabIndex={0}
role="region"
aria-label="Carousel"
>
<div className="carousel-container"> <div className="carousel-container">
<div className="carousel-viewport"> <div className="carousel-viewport">
<div <div
ref={trackRef}
className="carousel-track" className="carousel-track"
style={{ style={{ transform: `translateX(-${activeIndex * 100}%)` }}
transform: `translateX(${transform}%)`,
transition: isDragging ? 'none' : undefined
}}
onTouchStart={handleTouchStart} onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd} onTouchEnd={handleTouchEnd}
> >
{React.Children.map(children, (child, i) => ( {React.Children.map(children, (child, i) => (
<div <div className="carousel-slide" key={i}>
className="carousel-slide"
key={i}
role="tabpanel"
aria-hidden={activeIndex !== i}
>
{child} {child}
</div> </div>
))} ))}