add eslint, bug fixes
This commit is contained in:
@@ -16,6 +16,7 @@ const t = useTranslations(lang)
|
||||
|
||||
<footer id="contacts" class="flex flex-col items-center gap-4 text-center md:gap-6">
|
||||
<script
|
||||
script-src:unsafe-inline
|
||||
is:inline
|
||||
type="text/javascript"
|
||||
charset="utf-8"
|
||||
|
||||
@@ -4,6 +4,7 @@ import { SwiperSlide } from 'swiper/react'
|
||||
import StarIconSrc from '@/assets/icons/StarIcon.svg'
|
||||
import { useTranslations } from '@/i18n/utils'
|
||||
import { SimpleSwiper } from './swiper/SimpleSwiper'
|
||||
import { isAboveBreakpoint } from '@/libs/breakpoint'
|
||||
|
||||
type Review = z.infer<ReturnType<typeof reviewsSchema>>
|
||||
type Author = Review['author']
|
||||
@@ -18,6 +19,7 @@ type ReviewsProps = {
|
||||
const REVIEWS_LINK =
|
||||
'https://yandex.ru/maps/213/moscow/?ll=37.469982%2C55.781185&mode=poi&poi%5Bpoint%5D=37.469074%2C55.781319&poi%5Buri%5D=ymapsbm1%3A%2F%2Forg%3Foid%3D19715751392%26yclid%3D10576664134676381695%26banner_id%3D1873406736351323707&tab=reviews&z=17.75'
|
||||
const swiperSlideClassName = '!h-auto md:max-w-[600px]'
|
||||
const isMd = isAboveBreakpoint('MD')
|
||||
|
||||
export const Reviews: React.FC<ReviewsProps> = ({ reviews, lang }) => {
|
||||
const t = useTranslations(lang)
|
||||
@@ -25,11 +27,10 @@ export const Reviews: React.FC<ReviewsProps> = ({ reviews, lang }) => {
|
||||
return (
|
||||
<SimpleSwiper
|
||||
slidesPerView={1}
|
||||
freeMode={false}
|
||||
freeMode={isMd}
|
||||
breakpoints={{
|
||||
768: {
|
||||
slidesPerView: 'auto',
|
||||
freeMode: true,
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { useTranslations } from '@/i18n/utils'
|
||||
import type { CollectionEntry } from 'astro:content'
|
||||
import rightArrowIconSrc from '@/assets/icons/RightArrowIcon.svg'
|
||||
|
||||
export type CoachCardProps = CollectionEntry<'coaches'> & { lang?: string }
|
||||
|
||||
export const CoachCard: React.FC<CoachCardProps> = ({ data, lang, slug }) => {
|
||||
const { name, description, cover, coverAlt } = data
|
||||
const t = useTranslations(lang)
|
||||
export const CoachCard: React.FC<CoachCardProps> = ({ data, slug }) => {
|
||||
const { name, cover, coverAlt } = data
|
||||
const coachPageLink = `/coaches/${slug}`
|
||||
|
||||
return (
|
||||
<div className="aspect-3/4 relative h-full max-w-[262px] overflow-hidden rounded-3xl text-white md:max-w-[320px] lg:max-w-[400px]">
|
||||
<div className="relative aspect-3/4 h-full max-w-[262px] overflow-hidden rounded-3xl text-white md:max-w-[320px] lg:max-w-[400px]">
|
||||
<img
|
||||
{...cover}
|
||||
loading="lazy"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CollectionEntry, z } from 'astro:content'
|
||||
import type { CollectionEntry } from 'astro:content'
|
||||
import { SimpleSwiper } from '../swiper/SimpleSwiper'
|
||||
import { SwiperSlide } from 'swiper/react'
|
||||
import { CoachCard } from './CoachCard'
|
||||
|
||||
@@ -66,7 +66,7 @@ const MobileNavigation: React.FC<MobileNavigationProps> = ({ navItems }) => {
|
||||
<AnimatedBurgerButton isOpen={isOpen} onClick={toggleMenu} />
|
||||
<div
|
||||
className={cn(
|
||||
'fixed left-0 top-0 z-50 w-full h-[100dvh] transform bg-bg-main p-8 pt-16 transition-transform duration-300 ease-in-out',
|
||||
'fixed left-0 top-0 z-50 h-[100dvh] w-full transform bg-bg-main p-8 pt-16 transition-transform duration-300 ease-in-out',
|
||||
isOpen ? '-translate-x-0' : 'translate-x-full'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { servicesSchema } from '@/content.config'
|
||||
import type { z } from 'astro:content'
|
||||
import { useState } from 'react'
|
||||
import { SwiperSlide } from 'swiper/react'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { type SwiperRef, SwiperSlide } from 'swiper/react'
|
||||
import { CharachteristicCard } from './CharachteristicCard'
|
||||
import { cn } from '@/libs/cn'
|
||||
import { SimpleSwiper } from '../swiper/SimpleSwiper'
|
||||
@@ -11,9 +11,14 @@ export type ServicesProps = {
|
||||
}
|
||||
|
||||
export const Services: React.FC<ServicesProps> = ({ services }) => {
|
||||
const swiperRef = useRef<SwiperRef | null>(null)
|
||||
const [serviceId, setServiceId] = useState(0)
|
||||
const selectedService = services[serviceId]
|
||||
const isSelected = (index: number) => index === serviceId
|
||||
const updateServiceHandler = useCallback((index: number) => {
|
||||
setServiceId(index)
|
||||
swiperRef.current?.swiper.slideTo(0)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-5">
|
||||
@@ -22,14 +27,14 @@ export const Services: React.FC<ServicesProps> = ({ services }) => {
|
||||
<SwiperSlide style={{ width: 'auto' }} key={index}>
|
||||
<button
|
||||
className={cn('tag-btn', isSelected(index) && 'tag-btn-active')}
|
||||
onClick={() => setServiceId(index)}
|
||||
onClick={() => updateServiceHandler(index)}
|
||||
>
|
||||
{service.title}
|
||||
</button>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</SimpleSwiper>
|
||||
<SimpleSwiper>
|
||||
<SimpleSwiper ref={swiperRef}>
|
||||
{selectedService.charachteristics?.map((charachteristic, index) => (
|
||||
<SwiperSlide
|
||||
className="aspect-3/4 !h-auto w-full max-w-[262px] md:max-w-[320px]"
|
||||
|
||||
@@ -18,6 +18,7 @@ export const MediaSwiper = React.forwardRef<SwiperRef, MediaSwiperProps>(
|
||||
ref={ref}
|
||||
modules={[Navigation, Pagination, A11y, ...modules]}
|
||||
slidesPerView={1}
|
||||
spaceBetween={24}
|
||||
pagination={{
|
||||
clickable: true,
|
||||
}}
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
import { FreeMode, Mousewheel } from 'swiper/modules'
|
||||
import { Swiper, type SwiperProps } from 'swiper/react'
|
||||
import { Swiper, type SwiperProps, type SwiperRef } from 'swiper/react'
|
||||
import 'swiper/css'
|
||||
import 'swiper/css/free-mode'
|
||||
import './simple-swiper.css'
|
||||
import { cn } from '@/libs/cn'
|
||||
import React from 'react'
|
||||
|
||||
export const SimpleSwiper: React.FC<SwiperProps> = ({ children, className, ...props }) => (
|
||||
<Swiper
|
||||
direction="horizontal"
|
||||
slidesPerView="auto"
|
||||
freeMode={true}
|
||||
spaceBetween={24}
|
||||
mousewheel={{ enabled: true, forceToAxis: true }}
|
||||
modules={[FreeMode, Mousewheel]}
|
||||
{...props}
|
||||
className={cn('simple-swiper', className)}
|
||||
>
|
||||
{children}
|
||||
</Swiper>
|
||||
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>
|
||||
)
|
||||
)
|
||||
SimpleSwiper.displayName = 'SimpleSwiper'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineCollection, reference, z, type ImageFunction } from 'astro:content'
|
||||
import { defineCollection, z, type ImageFunction } from 'astro:content'
|
||||
|
||||
export const socialMediaLinkSchema = z.object({
|
||||
icon: z.string(),
|
||||
|
||||
@@ -8,6 +8,7 @@ export function getLangFromUrl(url: URL) {
|
||||
|
||||
if (lang in ui) return lang as keyof typeof ui
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return i18n!.defaultLocale
|
||||
}
|
||||
|
||||
|
||||
33
src/libs/breakpoint.ts
Normal file
33
src/libs/breakpoint.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { TW_WIDTHS } from '@/constants/images'
|
||||
|
||||
type Breakpoint = keyof typeof TW_WIDTHS
|
||||
|
||||
export function getBreakpoint(): Breakpoint {
|
||||
if (typeof window === 'undefined') return 'XXS'
|
||||
|
||||
const width = window.innerWidth
|
||||
|
||||
if (width >= TW_WIDTHS._2XL) return '_2XL'
|
||||
if (width >= TW_WIDTHS.XL) return 'XL'
|
||||
if (width >= TW_WIDTHS.LG) return 'LG'
|
||||
if (width >= TW_WIDTHS.MD) return 'MD'
|
||||
if (width >= TW_WIDTHS.SM) return 'SM'
|
||||
if (width >= TW_WIDTHS.XS) return 'XS'
|
||||
return 'XXS'
|
||||
}
|
||||
|
||||
export function isAboveBreakpoint(size: Breakpoint): boolean {
|
||||
const currentBreakpoint = getBreakpoint()
|
||||
const breakpoints = Object.keys(TW_WIDTHS) as Breakpoint[]
|
||||
const currentIndex = breakpoints.indexOf(currentBreakpoint)
|
||||
const targetIndex = breakpoints.indexOf(size)
|
||||
return currentIndex >= targetIndex
|
||||
}
|
||||
|
||||
export function isBelowBreakpoint(size: Breakpoint): boolean {
|
||||
const currentBreakpoint = getBreakpoint()
|
||||
const breakpoints = Object.keys(TW_WIDTHS) as Breakpoint[]
|
||||
const currentIndex = breakpoints.indexOf(currentBreakpoint)
|
||||
const targetIndex = breakpoints.indexOf(size)
|
||||
return currentIndex < targetIndex
|
||||
}
|
||||
@@ -98,7 +98,7 @@ const t = useTranslations(lang)
|
||||
{
|
||||
optimizedReviews && (
|
||||
<Section id="reviews" title={t('reviews.title')}>
|
||||
<Reviews client:visible reviews={optimizedReviews} lang={lang} />
|
||||
<Reviews client:only="react" reviews={optimizedReviews} lang={lang} />
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
@apply rounded-full border border-brand bg-transparent px-6 py-3 text-center text-text-light transition-colors duration-300 hover:bg-brand;
|
||||
}
|
||||
.tag-btn {
|
||||
@apply text-sm hover:tag-btn-active rounded-full bg-card p-2 transition-colors duration-300;
|
||||
@apply hover:tag-btn-active rounded-full bg-card p-2 text-sm transition-colors duration-300;
|
||||
}
|
||||
.tag-btn-active {
|
||||
@apply bg-brand text-text-light;
|
||||
|
||||
Reference in New Issue
Block a user