From 891b9ec5f777490a5382478d82f447dbe2b7a8e1 Mon Sep 17 00:00:00 2001
From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com>
Date: Thu, 2 Nov 2023 13:34:38 +0300
Subject: [PATCH] Use Solid Swiper (#293)
Use Solid Swiper
---
src/components/Article/FullArticle.tsx | 4 +-
src/components/Views/Edit.tsx | 4 +-
src/components/Views/Home.tsx | 36 +--
src/components/Views/Topic.module.scss | 0
src/components/Views/Topic.tsx | 35 +--
src/components/_shared/Slider/Slider.scss | 253 ------------------
src/components/_shared/Slider/Slider.tsx | 157 -----------
src/components/_shared/Slider/index.ts | 1 -
.../_shared/SolidSwiper/ArticleCardSwiper.tsx | 97 +++++++
.../{SolidSwiper.tsx => ImageSwiper.tsx} | 5 +-
.../_shared/SolidSwiper/Swiper.module.scss | 15 ++
src/components/_shared/SolidSwiper/index.ts | 2 +-
.../_shared/SolidSwiper/swiper.d.ts | 7 +-
13 files changed, 131 insertions(+), 485 deletions(-)
create mode 100644 src/components/Views/Topic.module.scss
delete mode 100644 src/components/_shared/Slider/Slider.scss
delete mode 100644 src/components/_shared/Slider/Slider.tsx
delete mode 100644 src/components/_shared/Slider/index.ts
create mode 100644 src/components/_shared/SolidSwiper/ArticleCardSwiper.tsx
rename src/components/_shared/SolidSwiper/{SolidSwiper.tsx => ImageSwiper.tsx} (99%)
diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx
index 4986b99b..5e742829 100644
--- a/src/components/Article/FullArticle.tsx
+++ b/src/components/Article/FullArticle.tsx
@@ -20,7 +20,7 @@ import { AudioHeader } from './AudioHeader'
import { Popover } from '../_shared/Popover'
import { VideoPlayer } from '../_shared/VideoPlayer'
import { Icon } from '../_shared/Icon'
-import { SolidSwiper } from '../_shared/SolidSwiper'
+import { ImageSwiper } from '../_shared/SolidSwiper'
import styles from './Article.module.scss'
import { CardTopic } from '../Feed/CardTopic'
import { createPopper } from '@popperjs/core'
@@ -331,7 +331,7 @@ export const FullArticle = (props: Props) => {
diff --git a/src/components/Views/Edit.tsx b/src/components/Views/Edit.tsx
index f24e6f26..d9e2f297 100644
--- a/src/components/Views/Edit.tsx
+++ b/src/components/Views/Edit.tsx
@@ -12,7 +12,7 @@ import { GrowingTextarea } from '../_shared/GrowingTextarea'
import { VideoUploader } from '../Editor/VideoUploader'
import { AudioUploader } from '../Editor/AudioUploader'
import { slugify } from '../../utils/slugify'
-import { SolidSwiper } from '../_shared/SolidSwiper'
+import { ImageSwiper } from '../_shared/SolidSwiper'
import { DropArea } from '../_shared/DropArea'
import { LayoutType, MediaItem } from '../../pages/types'
import { clone } from '../../utils/clone'
@@ -384,7 +384,7 @@ export const EditView = (props: Props) => {
- {
/>
-
-
- {(a) => (
-
- )}
-
-
+
@@ -162,21 +146,7 @@ export const HomeView = (props: Props) => {
{randomLayout()}
-
-
- {(a: Shout) => (
-
- )}
-
-
+
{
wrapper={'author'}
/>
-
-
- {(a: Shout) => (
-
- )}
-
-
+
{
15}>
-
-
- {(a: Shout) => (
-
- )}
-
-
-
+
diff --git a/src/components/_shared/Slider/Slider.scss b/src/components/_shared/Slider/Slider.scss
deleted file mode 100644
index 1aa7d959..00000000
--- a/src/components/_shared/Slider/Slider.scss
+++ /dev/null
@@ -1,253 +0,0 @@
-.swiper-slide {
- min-height: 0 !important;
-
- .cards-with-cover & {
- height: 0 !important;
- padding-top: 100%;
-
- @include media-breakpoint-up(sm) {
- padding-top: 56.2% !important;
- }
-
- @include media-breakpoint-up(md) {
- padding-top: 35% !important;
- }
-
- img {
- height: 100%;
- left: 0;
- object-fit: cover;
- object-position: center;
- position: absolute;
- top: 0;
- width: 100%;
- }
- }
-}
-
-.slider-arrow-prev,
-.slider-arrow-next {
- align-items: center;
- display: flex;
- cursor: pointer;
- height: 100%;
- position: absolute;
- outline: none;
- border: 0;
- transform: translate(0);
- top: 0;
- width: 21%;
- z-index: 1;
-
- @include media-breakpoint-down(md) {
- width: 8%;
- }
-
- &::after {
- color: #fff;
- }
-
- &:hover {
- .icon {
- opacity: 0.5;
- }
- }
-
- .icon {
- height: 36px;
- opacity: 1;
- transition: opacity 0.2s;
- width: 22px;
- }
-}
-
-.slider-arrow-prev {
- background: linear-gradient(to left, rgb(0 0 0 / 0%) 0%, rgb(0 0 0 / 90%) 100%);
- justify-content: flex-start;
- left: 0;
-
- &::after {
- margin-left: 5rem;
- }
-
- .icon {
- margin-left: 5rem;
-
- @include media-breakpoint-down(md) {
- margin-left: 25%;
- }
- }
-}
-
-.slider-arrow-next {
- background: linear-gradient(to left, rgb(0 0 0 / 90%) 0%, rgb(0 0 0 / 0%) 100%);
- justify-content: flex-end;
- right: 0;
-
- &::after {
- margin-right: 5rem;
- }
-
- .icon {
- margin-right: 5rem;
- transform: rotate(180deg);
-
- @include media-breakpoint-down(md) {
- margin-right: 25%;
- }
- }
-}
-
-.swiper--page-gallery {
- padding-bottom: 4rem;
-
- .swiper-wrapper {
- align-items: center;
- }
-
- .swiper-slide {
- display: flex;
- justify-content: center;
- }
-
- .swiper-slide__inner {
- display: flex;
- flex-direction: column;
- justify-content: center;
-
- img {
- display: block;
- height: auto;
- margin: 0 auto;
- max-height: 80vh;
- width: auto;
- position: relative;
- z-index: 11;
- }
- }
-
- .slider-arrow-prev,
- .slider-arrow-next {
- background: rgb(0 0 0 / 20%);
- width: 5rem;
- }
-
- .slider-arrow-next .icon {
- margin-right: 2rem;
- }
-
- .slider-arrow-prev .icon {
- margin-left: 2rem;
- }
-
- .swiper-slide-active {
- .swiper-lazy-preloader {
- display: none;
- }
- }
-}
-
-.thumbs-container {
- @include media-breakpoint-up(md) {
- padding-left: 3.2rem;
- }
-}
-
-.swiper--thumbs {
- @include media-breakpoint-up(md) {
- max-height: 80vh;
- min-width: 100px;
- padding: 0 !important;
- width: 100px !important;
- }
-
- .swiper-slide {
- cursor: pointer;
- height: 80px;
- opacity: 0.5;
- width: 100px;
-
- @include media-breakpoint-up(md) {
- height: 52px;
- width: auto;
- }
-
- img {
- height: 100%;
- object-fit: cover;
- object-position: center;
- width: 100%;
- }
- }
-
- .swiper-slide-thumb-active {
- opacity: 1;
- }
-
- .swiper-slide__inner {
- height: 100%;
- flex: 1;
- }
-
- .swiper-lazy-preloader,
- .image-description {
- display: none;
- }
-}
-
-.sliders-container {
- @include media-breakpoint-up(md) {
- display: flex;
- }
-}
-
-.swiper-pagination {
- background: #141414;
- bottom: 0;
- font-size: 1.2rem;
- font-weight: bold;
- left: auto;
- right: 0;
- padding: 1rem;
- width: auto;
-}
-
-.uploadPreview {
- background: unset;
- position: relative;
- padding: 0 40px;
-
- .sliders-container {
- position: relative;
- }
-
- .swiper {
- background: unset;
-
- .swiper-wrapper {
- min-height: 400px;
- }
- }
-
- .slider-arrow-next,
- .slider-arrow-prev {
- background: none;
- filter: invert(1);
- width: 40px;
- max-height: 540px;
-
- .icon {
- margin: auto;
- width: 12px;
- height: 20px;
- }
- }
-
- //
- //.slider-arrow-prev {
- // margin-left: -40px;
- //}
- //.slider-arrow-next {
- // margin-right: -40px;
- //}
-}
diff --git a/src/components/_shared/Slider/Slider.tsx b/src/components/_shared/Slider/Slider.tsx
deleted file mode 100644
index 6410940b..00000000
--- a/src/components/_shared/Slider/Slider.tsx
+++ /dev/null
@@ -1,157 +0,0 @@
-//TODO: Replace with SolidSwiper.tsx
-
-import { Swiper, Navigation, Pagination, Thumbs } from 'swiper'
-import type { SwiperOptions } from 'swiper'
-import 'swiper/scss'
-import 'swiper/scss/navigation'
-import 'swiper/scss/pagination'
-import 'swiper/scss/thumbs'
-import './Slider.scss'
-import { createEffect, createSignal, JSX, on, Show } from 'solid-js'
-import { Icon } from '../Icon'
-import { clsx } from 'clsx'
-
-interface Props {
- title?: string
- slidesPerView?: number
- isCardsWithCover?: boolean
- children?: JSX.Element
- isPageGallery?: boolean
- hasThumbs?: boolean
- variant?: 'uploadPreview'
- slideIndex?: (value: number) => void
-}
-
-export const Slider = (props: Props) => {
- let el: HTMLDivElement | undefined
- let thumbsEl: HTMLDivElement | undefined
- let nextEl: HTMLDivElement | undefined
- let prevEl: HTMLDivElement | undefined
-
- const isCardsWithCover = typeof props.isCardsWithCover === 'boolean' ? props.isCardsWithCover : true
-
- const [swiper, setSwiper] = createSignal()
- const [swiperThumbs, setSwiperThumbs] = createSignal()
- const opts: SwiperOptions = {
- observer: true,
- observeParents: true,
- roundLengths: true,
- loop: true,
- centeredSlides: true,
- slidesPerView: 1,
- modules: [Navigation, Pagination, Thumbs],
- speed: 500,
- on: {
- slideChange: () => {
- if (swiper()) {
- props.slideIndex(swiper().realIndex || 0)
- }
- }
- },
- navigation: { nextEl, prevEl },
- breakpoints: {
- 768: {
- slidesPerView: props.slidesPerView > 0 ? props.slidesPerView : 1.66666,
- spaceBetween: isCardsWithCover ? 8 : 26
- },
- 992: {
- slidesPerView: props.slidesPerView > 0 ? props.slidesPerView : 1.66666,
- spaceBetween: isCardsWithCover ? 8 : 52
- }
- },
- thumbs: {
- swiper: swiperThumbs()
- }
- }
-
- createEffect(() => {
- if (props.hasThumbs && !!thumbsEl) {
- setSwiperThumbs(
- new Swiper(thumbsEl, {
- slidesPerView: 'auto',
- modules: [Thumbs],
- roundLengths: true,
- spaceBetween: 20,
- freeMode: true,
- breakpoints: {
- 768: {
- direction: 'vertical'
- }
- }
- })
- )
- }
- })
-
- createEffect(() => {
- if (!swiper() && !!el) {
- if (swiperThumbs()) {
- opts.thumbs = {
- swiper: swiperThumbs()
- }
-
- opts.pagination = {
- el: '.swiper-pagination',
- type: 'fraction'
- }
- }
-
- setSwiper(new Swiper(el, opts))
- swiper().update()
- }
- })
-
- createEffect(() => {
- swiper().update()
- })
-
- return (
-
-
-
-
- {props.title}
-
-
-
-
-
{props.children}
-
- swiper()?.slideNext()}>
-
-
- swiper()?.slidePrev()}>
-
-
-
- {/**/}
-
-
-
-
-
-
-
-
-
- swiper()?.slideNext()}>
-
-
- swiper()?.slidePrev()}>
-
-
-
-
- )
-}
diff --git a/src/components/_shared/Slider/index.ts b/src/components/_shared/Slider/index.ts
deleted file mode 100644
index 6d43da3c..00000000
--- a/src/components/_shared/Slider/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { Slider } from './Slider'
diff --git a/src/components/_shared/SolidSwiper/ArticleCardSwiper.tsx b/src/components/_shared/SolidSwiper/ArticleCardSwiper.tsx
new file mode 100644
index 00000000..ed732d51
--- /dev/null
+++ b/src/components/_shared/SolidSwiper/ArticleCardSwiper.tsx
@@ -0,0 +1,97 @@
+import { createSignal, For, Show } from 'solid-js'
+import { Icon } from '../Icon'
+import { register } from 'swiper/element/bundle'
+import SwiperCore, { Manipulation, Navigation, Pagination } from 'swiper'
+import { SwiperRef } from './swiper'
+import { clsx } from 'clsx'
+import styles from './Swiper.module.scss'
+import { Shout } from '../../../graphql/types.gen'
+import { ArticleCard } from '../../Feed/ArticleCard'
+
+type Props = {
+ slides: Shout[]
+ slidesPerView?: number
+ title?: string
+}
+
+register()
+
+SwiperCore.use([Pagination, Navigation, Manipulation])
+
+export const ArticleCardSwiper = (props: Props) => {
+ const [slideIndex, setSlideIndex] = createSignal(0)
+
+ const mainSwipeRef: { current: SwiperRef } = { current: null }
+
+ const handleSlideChange = () => {
+ setSlideIndex(mainSwipeRef.current.swiper.activeIndex)
+ }
+
+ return (
+
+
+ {props.title}
+
+
+
0}>
+
+
(mainSwipeRef.current = el)}
+ centered-slides={true}
+ thumbs-swiper={'.thumbSwiper'}
+ observer={true}
+ onSlideChange={handleSlideChange}
+ slides-per-view={props.slidesPerView ?? 1.5}
+ space-between={52}
+ breakpoints={{ 768: { spaceBetween: 26 }, 992: { spaceBetween: 52 } }}
+ loop={true}
+ speed={800}
+ autoplay={{
+ disableOnInteraction: false,
+ delay: 3000,
+ pauseOnMouseEnter: true
+ }}
+ >
+
+ {(slide, index) => (
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+
+
+
+ )}
+
+
+
mainSwipeRef.current.swiper.slidePrev()}
+ >
+
+
+
mainSwipeRef.current.swiper.slideNext()}
+ >
+
+
+
+ {slideIndex() + 1} / {props.slides.length}
+
+
+
+
+
+ )
+}
diff --git a/src/components/_shared/SolidSwiper/SolidSwiper.tsx b/src/components/_shared/SolidSwiper/ImageSwiper.tsx
similarity index 99%
rename from src/components/_shared/SolidSwiper/SolidSwiper.tsx
rename to src/components/_shared/SolidSwiper/ImageSwiper.tsx
index faad65d5..1146dca2 100644
--- a/src/components/_shared/SolidSwiper/SolidSwiper.tsx
+++ b/src/components/_shared/SolidSwiper/ImageSwiper.tsx
@@ -1,4 +1,4 @@
-import { createEffect, createSignal, For, Show, on } from 'solid-js'
+import { createEffect, createSignal, For, Show, on, JSXElement } from 'solid-js'
import { MediaItem, UploadedFile } from '../../../pages/types'
import { Icon } from '../Icon'
import { Popover } from '../Popover'
@@ -32,7 +32,7 @@ register()
SwiperCore.use([Pagination, Navigation, Manipulation])
-export const SolidSwiper = (props: Props) => {
+export const ImageSwiper = (props: Props) => {
const { t } = useLocalize()
const [loading, setLoading] = createSignal(false)
const [slideIndex, setSlideIndex] = createSignal(0)
@@ -68,7 +68,6 @@ export const SolidSwiper = (props: Props) => {
{ defer: true }
)
)
-
const handleDropAreaUpload = (value: UploadedFile[]) => {
props.onImagesAdd(composeMediaItems(value))
swipeToUploaded()
diff --git a/src/components/_shared/SolidSwiper/Swiper.module.scss b/src/components/_shared/SolidSwiper/Swiper.module.scss
index 44ecd210..4260a522 100644
--- a/src/components/_shared/SolidSwiper/Swiper.module.scss
+++ b/src/components/_shared/SolidSwiper/Swiper.module.scss
@@ -13,6 +13,17 @@ $navigation-reserve: 32px;
margin: 2rem 0;
flex-direction: column;
+ &.ArticleCardSwiper {
+ margin-bottom: 6rem;
+ }
+
+ .sliderTitle {
+ @include font-size(4.5rem);
+
+ text-align: center;
+ padding: 4rem 0 0;
+ }
+
&.articleMode {
background: var(--background-color-invert);
color: var(--default-color-invert);
@@ -114,6 +125,10 @@ $navigation-reserve: 32px;
overflow: hidden;
width: calc(100% - 130px);
+ @include media-breakpoint-down(sm) {
+ width: 100%;
+ }
+
.counter {
@include font-size(1.2rem);
diff --git a/src/components/_shared/SolidSwiper/index.ts b/src/components/_shared/SolidSwiper/index.ts
index fbd847e0..54c84efc 100644
--- a/src/components/_shared/SolidSwiper/index.ts
+++ b/src/components/_shared/SolidSwiper/index.ts
@@ -1 +1 @@
-export { SolidSwiper } from './SolidSwiper'
+export { ImageSwiper } from './ImageSwiper'
diff --git a/src/components/_shared/SolidSwiper/swiper.d.ts b/src/components/_shared/SolidSwiper/swiper.d.ts
index 1da88ad9..d94ccd21 100644
--- a/src/components/_shared/SolidSwiper/swiper.d.ts
+++ b/src/components/_shared/SolidSwiper/swiper.d.ts
@@ -1,5 +1,5 @@
import 'solid-js'
-import { SwiperOptions } from 'swiper'
+import { SwiperOptions, AutoplayOptions } from 'swiper'
import { SwiperSlideProps } from 'swiper/react'
type Kebab = T extends `${infer F}${infer R}`
@@ -37,6 +37,11 @@ declare module 'solid-js' {
onSlideChange?: () => void
onBeforeSlideChangeStart?: () => void
class?: string
+ breakpoints?: {
+ [width: number]: SwiperOptions
+ [ratio: string]: SwiperOptions
+ }
+ autoplay?: AutoplayOptions | boolean
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface SwiperSlideAttributes extends KebabObjectKeys {