refactor: Адаптив виджета переработан на использование container quries

This commit is contained in:
Ilia Mashkov
2025-11-23 16:09:23 +03:00
parent 92e0b474a4
commit 174547791b
4 changed files with 107 additions and 94 deletions

View File

@@ -33,27 +33,19 @@
} }
:global(.swiper) { :global(.swiper) {
@media (width <=768px) { @container timeframe-slider (width <=768px) {
padding: 0 20px; padding: 0 40px;
} }
} }
:global(.swiper-slide-next) { :global(.swiper-slide-visible) {
transition: opacity 0.3s ease; transition: opacity 0.3s ease;
@media (width <=768px) { @container timeframe-slider (width <768px) {
opacity: 0.4; opacity: 0.4;
} }
} }
:global(.swiper-slide-prev) { :global(.swiper-slide-fully-visible) {
transition: opacity 0.3s ease;
@media (width <=768px) {
opacity: 0.4;
}
}
:global(.swiper-slide-active) {
opacity: 1; opacity: 1;
} }

View File

@@ -16,7 +16,7 @@ export const EVENT_CAROUSEL_CONFIG: SwiperOptions = {
}, },
breakpoints: { breakpoints: {
576: { 576: {
slidesPerView: 2, slidesPerView: 2.5,
}, },
768: { 768: {
slidesPerView: 2, slidesPerView: 2,

View File

@@ -1,3 +1,10 @@
/* Wrapper для container queries - должен быть родителем контейнера */
.wrapper {
/* Включаем container queries для адаптивности виджета */
container-type: inline-size;
container-name: timeframe-slider;
}
.container { .container {
position: relative; position: relative;
@@ -23,11 +30,11 @@
overflow: hidden; overflow: hidden;
@media (width <=1024px) { @container timeframe-slider (width <=1024px) {
padding-top: 100px; padding-top: 100px;
} }
@media (width <=768px) { @container timeframe-slider (width <=576px) {
padding: 60px 20px 20px; padding: 60px 20px 20px;
background-image: unset; background-image: unset;
@@ -51,20 +58,24 @@
border-image: var(--gradient-primary) 1; border-image: var(--gradient-primary) 1;
@media (width <=1024px) { @container timeframe-slider (width <=1024px) {
top: 80px; top: 80px;
font-size: 40px; font-size: 40px;
} }
@media (width <=768px) { @container timeframe-slider (width <=768px) {
padding-left: 20px;
font-size: 34px;
}
@container timeframe-slider (width <=576px) {
position: relative; position: relative;
inset: unset; inset: unset;
margin-bottom: 20px; margin-bottom: 20px;
padding-left: 0; padding-left: 0;
font-size: 20px;
border: none; border: none;
} }
@@ -85,7 +96,7 @@
background-position: center; background-position: center;
background-size: 100% 1px; background-size: 100% 1px;
@media (width <=768px) { @container timeframe-slider (width <=576px) {
position: unset; position: unset;
display: flex; display: flex;
@@ -111,19 +122,22 @@
transform-origin: left; transform-origin: left;
@media (width <=1024px) { @container timeframe-slider (width <=1024px) {
left: 100px; left: 100px;
bottom: 40px; bottom: 40px;
} }
@media (width <=768px) { @container timeframe-slider (width <=768px) {
left: 40px;
gap: 10px;
}
@container timeframe-slider (width <=576px) {
left: 20px; left: 20px;
bottom: 13px; bottom: 13px;
order: 2; order: 2;
gap: 10px;
margin-top: 20px; margin-top: 20px;
padding: 0; padding: 0;
} }
@@ -138,7 +152,7 @@
display: flex; display: flex;
gap: 20px; gap: 20px;
@media (width <=768px) { @container timeframe-slider (width <=768px) {
gap: 8px; gap: 8px;
} }
} }
@@ -147,7 +161,7 @@
width: 9px; width: 9px;
height: 14px; height: 14px;
@media (width <=768px) { @container timeframe-slider (width <=576px) {
width: 6px; width: 6px;
height: 11.5px; height: 11.5px;
} }
@@ -156,7 +170,7 @@
.dots { .dots {
display: none; display: none;
@media (width <=768px) { @container timeframe-slider (width <=576px) {
position: absolute; position: absolute;
left: 50%; left: 50%;
bottom: 32px; bottom: 32px;
@@ -214,14 +228,19 @@
pointer-events: none; pointer-events: none;
@media (width <=1024px) { @container timeframe-slider (width <=1024px) {
gap: 40px; gap: 40px;
font-size: 140px; font-size: 140px;
line-height: 120px; line-height: 120px;
} }
@media (width <=768px) { @container timeframe-slider (width <=768px) {
font-size: 100px;
line-height: 80px;
}
@container timeframe-slider (width <=576px) {
position: static; position: static;
gap: 20px; gap: 20px;
@@ -246,7 +265,7 @@
.periodLabel { .periodLabel {
display: none; display: none;
@media (width <=768px) { @container timeframe-slider (width <=576px) {
order: 1; order: 1;
display: block; display: block;
@@ -266,7 +285,7 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
@media (width <=768px) { @container timeframe-slider (width <=576px) {
display: none; display: none;
} }
} }
@@ -274,7 +293,7 @@
.carouselContainer { .carouselContainer {
padding: 55px 80px 105px; padding: 55px 80px 105px;
@media (width <=768px) { @container timeframe-slider (width <=768px) {
width: calc(100% + 40px); width: calc(100% + 40px);
margin: 0 -20px; margin: 0 -20px;
padding: 0; padding: 0;

View File

@@ -132,74 +132,76 @@ export const TimeFrameSlider = memo(() => {
}, [totalPeriods]) }, [totalPeriods])
return ( return (
<div className={styles.container} ref={containerRef}> <div className={styles.wrapper}>
<h1 className={styles.title}>Исторические даты</h1> <div className={styles.container} ref={containerRef}>
<h1 className={styles.title}>Исторические даты</h1>
<div className={styles.content}> <div className={styles.content}>
<div className={styles.centerDate}> <div className={styles.centerDate}>
<span ref={startYearRef}>{currentPeriod.yearFrom}</span> <span ref={startYearRef}>{currentPeriod.yearFrom}</span>
<span ref={endYearRef}>{currentPeriod.yearTo}</span> <span ref={endYearRef}>{currentPeriod.yearTo}</span>
</div>
<div className={styles.periodLabel} ref={periodLabelRef}>
{currentPeriod.label}
</div>
<div className={styles.circleContainer}>
<CircleTimeline
periods={HISTORICAL_PERIODS}
activeIndex={activePeriod}
onPeriodChange={setActivePeriod}
rotation={rotation}
/>
</div>
<div className={styles.controls}>
<div className={styles.pagination}>
{String(activePeriod + 1).padStart(2, '0')}/
{String(totalPeriods).padStart(2, '0')}
</div> </div>
<div className={styles.buttons}>
<Button <div className={styles.periodLabel} ref={periodLabelRef}>
variant='round' {currentPeriod.label}
size='medium' </div>
colorScheme='primary'
onClick={handlePrev} <div className={styles.circleContainer}>
aria-label='Предыдущий период' <CircleTimeline
> periods={HISTORICAL_PERIODS}
<ChevronSvg className={styles.chevronIcon} stroke='#42567A' /> activeIndex={activePeriod}
</Button> onPeriodChange={setActivePeriod}
<Button rotation={rotation}
variant='round' />
size='medium' </div>
colorScheme='primary'
onClick={handleNext} <div className={styles.controls}>
aria-label='Следующий период' <div className={styles.pagination}>
> {String(activePeriod + 1).padStart(2, '0')}/
<ChevronSvg {String(totalPeriods).padStart(2, '0')}
className={classNames(styles.chevronIcon, styles.rotated)} </div>
stroke='#42567A' <div className={styles.buttons}>
/> <Button
</Button> variant='round'
size='medium'
colorScheme='primary'
onClick={handlePrev}
aria-label='Предыдущий период'
>
<ChevronSvg className={styles.chevronIcon} stroke='#42567A' />
</Button>
<Button
variant='round'
size='medium'
colorScheme='primary'
onClick={handleNext}
aria-label='Следующий период'
>
<ChevronSvg
className={classNames(styles.chevronIcon, styles.rotated)}
stroke='#42567A'
/>
</Button>
</div>
</div> </div>
</div> </div>
</div>
<div className={styles.carouselContainer}> <div className={styles.carouselContainer}>
<EventsCarousel events={currentPeriod.events} visible /> <EventsCarousel events={currentPeriod.events} visible />
</div> </div>
<div className={styles.dots}> <div className={styles.dots}>
{HISTORICAL_PERIODS.map((_, index) => ( {HISTORICAL_PERIODS.map((_, index) => (
<button <button
key={index} key={index}
className={classNames(styles.dot, { className={classNames(styles.dot, {
[styles.activeDot]: index === activePeriod, [styles.activeDot]: index === activePeriod,
})} })}
onClick={() => setActivePeriod(index)} onClick={() => setActivePeriod(index)}
aria-label={`Перейти к периоду ${index + 1}`} aria-label={`Перейти к периоду ${index + 1}`}
/> />
))} ))}
</div>
</div> </div>
</div> </div>
) )