fix slider

This commit is contained in:
Salam-vsem
2025-05-20 16:58:44 +03:00
parent 8a8583fc8f
commit d774f0345f
11 changed files with 158 additions and 112 deletions

View File

@@ -0,0 +1,24 @@
import { forwardRef } from 'react'
import type { IconProps } from './types'
export const ArrowIcon = forwardRef<SVGSVGElement, IconProps>(function ArrowIcon(props, ref) {
return (
<svg
width="24px"
height="24px"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
ref={ref}
{...props}
>
<path
d="M6 12H18M6 12L11 7M6 12L11 17"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)
})

View File

@@ -15,23 +15,13 @@ const t = useTranslations(lang)
---
<footer id="contacts" class="flex flex-col items-center gap-4 text-center md:gap-6">
<div class="flex h-[400px] w-full items-center justify-center bg-bg-light">
<script
transition:persist
script-src:unsafe-inline
is:inline
type="text/javascript"
charset="utf-8"
async
onload="document.getElementById('ymap-loader').style.display = 'none';"
src=`https://api-maps.yandex.ru/services/constructor/1.0/js/?um=constructor%3A069b17526dc83cbf87c488f13ae089eb3af0f8f74af57e1be82e183434693562&width=100%25&height=400&lang=${lang}&scroll=false`
></script>
<div
id="ymap-loader"
class="h-16 w-16 animate-spin rounded-full border-6 border-brand border-t-transparent"
>
</div>
</div>
<iframe
transition:persist
src=`https://yandex.ru/map-widget/v1/?um=constructor%3A069b17526dc83cbf87c488f13ae089eb3af0f8f74af57e1be82e183434693562&source=constructor&scroll=false&lang=${lang}`
width="100%"
height="400"
onload="document.getElementById('ymap-loader').style.display = 'none';"
frameborder="0"></iframe>
<div class="flex flex-col items-center gap-3 px-4 py-6 text-center md:gap-4 md:px-6 md:py-8">
<Image src={logoSrc} alt="logo" class="h-32 w-auto md:h-48" />
<p class="text-light text-sm">

View File

@@ -29,6 +29,7 @@ export const Reviews: React.FC<ReviewsProps> = ({ reviews, lang }) => {
<SimpleSwiper
slidesPerView={1}
freeMode={isMd}
pagination={{type: 'fraction', enabled: !isMd}}
breakpoints={{
[TW_SCREENS.MD]: {
slidesPerView: 'auto',

View File

@@ -0,0 +1,41 @@
import React from 'react'
import { cn } from '@/libs/cn'
import type { PaginationOptions } from 'swiper/types'
import type { SwiperProps } from 'swiper/react'
import { ArrowIcon } from '@/assets/icons/ArrowIcon'
type FractionPaginationProps = React.HTMLAttributes<HTMLDivElement>
export const isFranctionPagination = (
options?: Pick<SwiperProps, 'pagination'>['pagination']
): options is PaginationOptions => typeof options === 'object' && options.type === 'fraction'
export const makeCustomFranctionOptions = (
options: PaginationOptions
): Pick<SwiperProps, 'pagination' | 'navigation'> => ({
pagination: {
...options,
type: 'fraction',
el: '#pagination-container',
},
navigation: {
prevEl: '#swiper-button-prev',
nextEl: '#swiper-button-next',
},
})
export const FractionPagination: React.FC<FractionPaginationProps> = ({ className, ...props }) => {
return (
<div
className={cn('mt-4 flex w-full items-center justify-center gap-6 lg:mt-8', className)}
{...props}
>
<button id="swiper-button-prev" className="transition-opacity duration-300">
<ArrowIcon className="stroke-text" height={32} width={32} />
</button>
<div className="font-semibold text-text" id="pagination-container"></div>
<button id="swiper-button-next" className="transition-opacity duration-300">
<ArrowIcon className="rotate-180 stroke-text" height={32} width={32} />
</button>
</div>
)
}

View File

@@ -1,11 +1,8 @@
import React from 'react'
import { Swiper, type SwiperProps, type SwiperRef, SwiperSlide } from 'swiper/react'
import { A11y, Navigation, Pagination } from 'swiper/modules'
import { type SwiperProps, type SwiperRef, SwiperSlide } from 'swiper/react'
import { cn } from '@/libs/cn'
import 'swiper/css'
import 'swiper/css/navigation'
import 'swiper/css/pagination'
import {isAboveBreakpoint} from '@/libs/breakpoint'
import { isAboveBreakpoint } from '@/libs/breakpoint'
import { SimpleSwiper } from './SimpleSwiper'
export type MediaSwiperProps = SwiperProps & {
images: ImportAttributes[]
@@ -16,19 +13,20 @@ export type MediaSwiperProps = SwiperProps & {
const isMd = isAboveBreakpoint('MD')
export const MediaSwiper = React.forwardRef<SwiperRef, MediaSwiperProps>(
({ images, className, modules = [], slideClassName, imageClassName, ...otherProps }, ref) => {
({ images, className, slideClassName, imageClassName, ...otherProps }, ref) => {
return (
<Swiper
<SimpleSwiper
ref={ref}
modules={[Navigation, Pagination, A11y, ...modules]}
slidesPerView={1}
freeMode={false}
mousewheel={false}
navigation={isMd}
spaceBetween={24}
pagination={{
clickable: true,
type: 'fraction',
}}
loop={true}
watchSlidesProgress={true}
style={{ overflow: 'hidden' }}
className={cn('h-full w-full', className)}
{...otherProps}
>
@@ -47,7 +45,7 @@ export const MediaSwiper = React.forwardRef<SwiperRef, MediaSwiperProps>(
<div className="swiper-lazy-preloader border-brand"></div>
</SwiperSlide>
))}
</Swiper>
</SimpleSwiper>
)
}
)

View File

@@ -1,21 +0,0 @@
import React, { useRef } from 'react'
import { type SwiperRef } from 'swiper/react'
import 'swiper/css'
import 'swiper/css/navigation'
import 'swiper/css/pagination'
import { NextButton, PrevButton } from './NavButtons'
import { useNavHandlers } from './hooks'
import { MediaSwiper, type MediaSwiperProps } from './MediaSwiper'
export const MediaSwiperWithNav: React.FC<MediaSwiperProps> = (props) => {
const swiperRef = useRef<SwiperRef | null>(null)
const { handleNext, handlePrev } = useNavHandlers(swiperRef)
return (
<div className="flex h-full w-full items-center gap-10">
<PrevButton className="hidden md:block" onClick={handlePrev} />
<MediaSwiper {...props} ref={swiperRef} />
<NextButton className="hidden md:block" onClick={handleNext} />
</div>
)
}

View File

@@ -1,30 +0,0 @@
import type { ImageMetadata } from 'astro'
import React, { type ButtonHTMLAttributes } from 'react'
import ChevronLeft from '@/assets/icons/LeftArrowIcon.svg'
import ChevronRight from '@/assets/icons/RightArrowIcon.svg'
import { cn } from '@/libs/cn'
type Props = ButtonHTMLAttributes<HTMLButtonElement> & { iconSrc: ImageMetadata }
export const NavButton: React.FC<Props> = ({ iconSrc, ...buttonProps }) => {
return (
<button
{...buttonProps}
className={cn(
'rounded-full border border-solid border-border-light',
buttonProps.disabled && 'border-none',
buttonProps.className
)}
>
<img {...iconSrc} width={50} height={50} />
</button>
)
}
export const PrevButton: React.FC<ButtonHTMLAttributes<HTMLButtonElement>> = (props) => {
return <NavButton {...props} iconSrc={ChevronLeft} />
}
export const NextButton: React.FC<ButtonHTMLAttributes<HTMLButtonElement>> = (props) => {
return <NavButton {...props} iconSrc={ChevronRight} />
}

View File

@@ -1,26 +1,36 @@
import { FreeMode, Mousewheel } from 'swiper/modules'
import { A11y, FreeMode, Mousewheel, Navigation, Pagination } from 'swiper/modules'
import { Swiper, type SwiperProps, type SwiperRef } from 'swiper/react'
import 'swiper/css'
import 'swiper/css/free-mode'
import 'swiper/css/pagination'
import 'swiper/css/navigation'
import './simple-swiper.css'
import { cn } from '@/libs/cn'
import React from 'react'
import { FractionPagination } from './FractionPagination'
import { isEnabledPagination, makeCustomPaginationOptions } from './utils'
export const SimpleSwiper = React.forwardRef<SwiperRef, SwiperProps>(
({ children, className, ...props }, ref) => (
<Swiper
direction="horizontal"
slidesPerView="auto"
freeMode={true}
spaceBetween={24}
mousewheel={{ enabled: true, forceToAxis: true }}
modules={[FreeMode, Mousewheel]}
{...props}
ref={ref}
className={cn('simple-swiper', className)}
>
{children}
</Swiper>
)
({ children, className, pagination, modules, ...props }, ref) => {
const paginationOptions = makeCustomPaginationOptions(pagination)
return (
<Swiper
direction="horizontal"
slidesPerView="auto"
freeMode={true}
spaceBetween={24}
mousewheel={{ enabled: true, forceToAxis: true }}
modules={[Navigation, Pagination, FreeMode, Mousewheel, A11y, ...(modules || [])]}
{...props}
{...paginationOptions}
ref={ref}
className={cn('simple-swiper', className)}
>
{children}
{isEnabledPagination(pagination) && <FractionPagination />}
</Swiper>
)
}
)
SimpleSwiper.displayName = 'SimpleSwiper'

View File

@@ -1,16 +0,0 @@
import { type RefObject, useCallback } from 'react'
import type { SwiperRef } from 'swiper/react'
export const useNavHandlers = (swiperRef: RefObject<SwiperRef | null>) => {
const handlePrev = useCallback(() => {
if (!swiperRef.current) return
swiperRef.current.swiper.slidePrev()
}, [])
const handleNext = useCallback(() => {
if (!swiperRef.current) return
swiperRef.current.swiper.slideNext()
}, [])
return { handlePrev, handleNext } as const
}

View File

@@ -2,3 +2,15 @@
width: 100%;
overflow: visible;
}
.swiper-pagination-fraction {
@apply text-xl w-auto;
}
.swiper-pagination-current {
@apply text-text-light;
}
.swiper-button-disabled {
@apply opacity-0;
}

View File

@@ -0,0 +1,37 @@
import type { SwiperProps } from 'swiper/react'
import type { PaginationOptions } from 'swiper/types'
export const isEnabledPagination = (
pagination?: Pick<SwiperProps, 'pagination'>['pagination']
): boolean =>
(typeof pagination === 'object' && pagination.enabled !== false) ||
(typeof pagination === 'boolean' && pagination)
export const isFranctionPagination = (
options?: Pick<SwiperProps, 'pagination'>['pagination']
): options is PaginationOptions => typeof options === 'object' && options.type === 'fraction'
export const makeCustomFranctionOptions = (
options: PaginationOptions
): Pick<SwiperProps, 'pagination' | 'navigation'> => ({
pagination: {
...options,
type: 'fraction',
el: '#pagination-container',
},
navigation: {
enabled: options.enabled,
prevEl: '#swiper-button-prev',
nextEl: '#swiper-button-next',
},
})
export const makeCustomPaginationOptions = (
pagination: Pick<SwiperProps, 'pagination'>['pagination']
): SwiperProps => {
if (!pagination) return { pagination: { enabled: Boolean(pagination) } }
if (isFranctionPagination(pagination)) return makeCustomFranctionOptions(pagination)
return { pagination }
}