fix: make image slider more beautiful
This commit is contained in:
parent
9e024ef1eb
commit
7aa901a355
6 changed files with 94 additions and 16 deletions
|
|
@ -1,12 +1,18 @@
|
||||||
import arrow from "./Arrow.svg"
|
import arrow from './Arrow.svg'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
|
|
||||||
type ArrowProps = {
|
type ArrowProps = {
|
||||||
direction: "left" | "right"
|
direction: 'left' | 'right',
|
||||||
|
onClick?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Arrow = ({direction}: ArrowProps) => {
|
export const Arrow = ({ direction, onClick }: ArrowProps) => {
|
||||||
return (
|
return (
|
||||||
<Image src={arrow} alt={""} style={{transform: `rotate(${direction === 'left' ? 0 : 180}deg)`}}/>
|
<Image
|
||||||
|
onClick={onClick}
|
||||||
|
src={arrow}
|
||||||
|
alt={''}
|
||||||
|
style={{ transform: `rotate(${direction === 'left' ? 0 : 180}deg)` }}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -8,7 +8,8 @@
|
||||||
box-shadow: 3px 7px 26px -5px rgba(0, 0, 0, 0.15);
|
box-shadow: 3px 7px 26px -5px rgba(0, 0, 0, 0.15);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: $border-radius;
|
border-top-left-radius: $border-radius;
|
||||||
|
border-top-right-radius: $border-radius;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17,12 +18,13 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0px;
|
bottom: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: $base-color;
|
color: $base-color;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
transition: padding 0.3s ease-out;
|
transition: padding 0.3s ease-out;
|
||||||
border-radius: $border-radius;
|
border-top-left-radius: $border-radius;
|
||||||
|
border-top-right-radius: $border-radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container:hover .title {
|
.container:hover .title {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,12 @@ export const Default: Story = {
|
||||||
src: "https://i1.sndcdn.com/artworks-6CIZtxsNOaYIm98h-mL41yA-t500x500.jpg",
|
src: "https://i1.sndcdn.com/artworks-6CIZtxsNOaYIm98h-mL41yA-t500x500.jpg",
|
||||||
title: "Erntedankfest",
|
title: "Erntedankfest",
|
||||||
href: "https://somelink"
|
href: "https://somelink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "id4",
|
||||||
|
src: "https://i1.sndcdn.com/artworks-6CIZtxsNOaYIm98h-mL41yA-t500x500.jpg",
|
||||||
|
title: "Slide4",
|
||||||
|
href: "https://somelink"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
import { ImageCard } from '@/components/ImageCard/ImageCard'
|
import { ImageCard } from '@/components/ImageCard/ImageCard'
|
||||||
import styles from "./styles.module.scss"
|
import styles from "./styles.module.scss"
|
||||||
import { StaticImageData } from 'next/image'
|
import { StaticImageData } from 'next/image'
|
||||||
import { Arrow } from '@/components/Arrow/Arrow'
|
import { Arrow } from '@/components/Arrow/Arrow'
|
||||||
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
|
||||||
export type Slide = {
|
export type Slide = {
|
||||||
id: string,
|
id: string,
|
||||||
|
|
@ -15,20 +19,59 @@ type ImageCardSliderProps = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ImageCardSlider = ({slides}: ImageCardSliderProps) => {
|
export const ImageCardSlider = ({slides}: ImageCardSliderProps) => {
|
||||||
|
|
||||||
|
const itemsPerPage = 3;
|
||||||
|
const [page, setPage] = useState(0)
|
||||||
|
const [prevDisabled, setPrevDisabled] = useState(true)
|
||||||
|
const [nextDisabled, setNextDisabled] = useState(true)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (page === 0) {
|
||||||
|
setPrevDisabled(true)
|
||||||
|
} else {
|
||||||
|
setPrevDisabled(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((page + 1) * itemsPerPage > slides.length) {
|
||||||
|
setNextDisabled(true)
|
||||||
|
} else {
|
||||||
|
setNextDisabled(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [slides, page, setNextDisabled, setPrevDisabled])
|
||||||
|
|
||||||
|
const pagedSlides = useMemo(() => {
|
||||||
|
return slides.slice(page * itemsPerPage, (page + 1) * itemsPerPage)
|
||||||
|
}, [slides, page])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
<div className={styles.arrowLeft}>
|
<div className={classNames({
|
||||||
<Arrow direction={"left"} />
|
[styles.arrowLeft]: true,
|
||||||
|
[styles.disabled]: prevDisabled,
|
||||||
|
})}>
|
||||||
|
<Arrow
|
||||||
|
direction={"left"}
|
||||||
|
onClick={() => setPage(page - 1)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
{
|
{
|
||||||
slides.map(slide => <div key={slide.id} className={styles.item}>
|
pagedSlides.map(slide =>
|
||||||
<ImageCard src={slide.src} title={slide.title} href={slide.href} />
|
<div key={slide.id} className={styles.item}>
|
||||||
</div>)
|
<ImageCard src={slide.src} title={slide.title} href={slide.href} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.arrowRight}>
|
<div className={classNames({
|
||||||
<Arrow direction={"right"} />
|
[styles.arrowRight]: true,
|
||||||
|
[styles.disabled]: nextDisabled,
|
||||||
|
})}>
|
||||||
|
<Arrow
|
||||||
|
direction={"right"}
|
||||||
|
onClick={() => setPage(page + 1)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,14 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(50% - 25px);
|
top: calc(50% - 25px);
|
||||||
left: -50px;
|
left: -50px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.arrowRight {
|
.arrowRight {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -50px;
|
right: -50px;
|
||||||
top: calc(50% - 25px);
|
top: calc(50% - 25px);
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
|
|
@ -24,8 +26,24 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
flex: 1 1 calc(33.333% - 20px);
|
flex: 0 1 calc(33.333% - 20px);
|
||||||
aspect-ratio: 1 / 1;
|
aspect-ratio: 1 / 1;
|
||||||
|
animation: fadein 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
pointer-events: none;
|
||||||
|
cursor: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadein {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 576px) {
|
@media screen and (max-width: 576px) {
|
||||||
|
|
@ -33,7 +51,9 @@
|
||||||
flex: 0 0 100%;
|
flex: 0 0 100%;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1200px) {
|
||||||
.arrowLeft, .arrowRight {
|
.arrowLeft, .arrowRight {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ export const fetchBlog = async (): Promise<PaginatedDocs<Blog> | undefined> => {
|
||||||
title: true,
|
title: true,
|
||||||
date: true,
|
date: true,
|
||||||
photo: true
|
photo: true
|
||||||
}
|
},
|
||||||
|
limit: 18
|
||||||
},
|
},
|
||||||
{ addQueryPrefix: true },
|
{ addQueryPrefix: true },
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue