Mobile view slider (#324)

* Mobile view slider
---------

Co-authored-by: kvakazyambra <kvakazyambra@gmail.com>
This commit is contained in:
Ilya Y 2023-12-04 13:05:29 +03:00 committed by GitHub
parent e782ef348b
commit ba71cbfdef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 423 additions and 231 deletions

View File

@ -16,7 +16,7 @@ import { slugify } from '../../utils/slugify'
import { DropArea } from '../_shared/DropArea' import { DropArea } from '../_shared/DropArea'
import { Icon } from '../_shared/Icon' import { Icon } from '../_shared/Icon'
import { Popover } from '../_shared/Popover' import { Popover } from '../_shared/Popover'
import { ImageSwiper } from '../_shared/SolidSwiper' import { EditorSwiper } from '../_shared/SolidSwiper'
import { Editor, Panel } from '../Editor' import { Editor, Panel } from '../Editor'
import { AudioUploader } from '../Editor/AudioUploader' import { AudioUploader } from '../Editor/AudioUploader'
import { AutoSaveNotice } from '../Editor/AutoSaveNotice' import { AutoSaveNotice } from '../Editor/AutoSaveNotice'
@ -369,8 +369,7 @@ export const EditView = (props: Props) => {
</div> </div>
<Show when={props.shout.layout === 'image'}> <Show when={props.shout.layout === 'image'}>
<ImageSwiper <EditorSwiper
editorMode={true}
images={mediaItems()} images={mediaItems()}
onImageChange={handleMediaChange} onImageChange={handleMediaChange}
onImageDelete={(index) => handleMediaDelete(index)} onImageDelete={(index) => handleMediaDelete(index)}

View File

@ -36,7 +36,7 @@ export const ArticleCardSwiper = (props: Props) => {
ref={(el) => (mainSwipeRef.current = el)} ref={(el) => (mainSwipeRef.current = el)}
centered-slides={true} centered-slides={true}
observer={true} observer={true}
space-between={20} space-between={10}
breakpoints={{ breakpoints={{
576: { spaceBetween: 20, slidesPerView: 1.5 }, 576: { spaceBetween: 20, slidesPerView: 1.5 },
992: { spaceBetween: 52, slidesPerView: 1.5 }, 992: { spaceBetween: 52, slidesPerView: 1.5 },
@ -44,13 +44,11 @@ export const ArticleCardSwiper = (props: Props) => {
round-lengths={true} round-lengths={true}
loop={true} loop={true}
speed={800} speed={800}
/*
autoplay={{ autoplay={{
disableOnInteraction: false, disableOnInteraction: false,
delay: 6000, delay: 6000,
pauseOnMouseEnter: true pauseOnMouseEnter: true,
}} }}
*/
> >
<For each={props.slides}> <For each={props.slides}>
{(slide, index) => ( {(slide, index) => (

View File

@ -0,0 +1,326 @@
import { createFileUploader } from '@solid-primitives/upload'
import { clsx } from 'clsx'
import { createEffect, createSignal, For, Show, on, onMount, lazy } from 'solid-js'
import SwiperCore, { Manipulation, Navigation, Pagination } from 'swiper'
import { useLocalize } from '../../../context/localize'
import { useSnackbar } from '../../../context/snackbar'
import { MediaItem, UploadedFile } from '../../../pages/types'
import { composeMediaItems } from '../../../utils/composeMediaItems'
import { getImageUrl } from '../../../utils/getImageUrl'
import { handleImageUpload } from '../../../utils/handleImageUpload'
import { validateFiles } from '../../../utils/validateFile'
import { DropArea } from '../DropArea'
import { Icon } from '../Icon'
import { Image } from '../Image'
import { Loading } from '../Loading'
import { Popover } from '../Popover'
import { SwiperRef } from './swiper'
import styles from './Swiper.module.scss'
const SimplifiedEditor = lazy(() => import('../../Editor/SimplifiedEditor'))
type Props = {
images: MediaItem[]
onImagesAdd?: (value: MediaItem[]) => void
onImagesSorted?: (value: MediaItem[]) => void
onImageDelete?: (mediaItemIndex: number) => void
onImageChange?: (index: number, value: MediaItem) => void
}
export const EditorSwiper = (props: Props) => {
const { t } = useLocalize()
const [loading, setLoading] = createSignal(false)
const [slideIndex, setSlideIndex] = createSignal(0)
const [slideBody, setSlideBody] = createSignal<string>()
const mainSwipeRef: { current: SwiperRef } = { current: null }
const thumbSwipeRef: { current: SwiperRef } = { current: null }
const {
actions: { showSnackbar },
} = useSnackbar()
const handleSlideDescriptionChange = (index: number, field: string, value) => {
if (props.onImageChange) {
props.onImageChange(index, { ...props.images[index], [field]: value })
}
}
const swipeToUploaded = () => {
setTimeout(() => {
mainSwipeRef.current.swiper.slideTo(props.images.length - 1)
}, 0)
}
const handleSlideChange = () => {
thumbSwipeRef.current.swiper.slideTo(mainSwipeRef.current.swiper.activeIndex)
setSlideIndex(mainSwipeRef.current.swiper.activeIndex)
}
createEffect(
on(
() => props.images.length,
() => {
mainSwipeRef.current?.swiper.update()
thumbSwipeRef.current?.swiper.update()
},
{ defer: true },
),
)
const handleDropAreaUpload = (value: UploadedFile[]) => {
props.onImagesAdd(composeMediaItems(value))
swipeToUploaded()
}
const handleDelete = (index: number) => {
props.onImageDelete(index)
if (index === 0) {
mainSwipeRef.current.swiper.update()
} else {
mainSwipeRef.current.swiper.slideTo(index - 1)
}
}
const { selectFiles } = createFileUploader({
multiple: true,
accept: `image/*`,
})
const initUpload = async (selectedFiles) => {
const isValid = validateFiles('image', selectedFiles)
if (!isValid) {
await showSnackbar({ type: 'error', body: t('Invalid file type') })
setLoading(false)
return
}
try {
setLoading(true)
const results: UploadedFile[] = []
for (const file of selectedFiles) {
const result = await handleImageUpload(file)
results.push(result)
}
props.onImagesAdd(composeMediaItems(results))
setLoading(false)
swipeToUploaded()
} catch (error) {
console.error('[runUpload]', error)
showSnackbar({ type: 'error', body: t('Error') })
setLoading(false)
}
}
const handleUploadThumb = async () => {
selectFiles((selectedFiles) => {
initUpload(selectedFiles)
})
}
const handleChangeIndex = (direction: 'left' | 'right', index: number) => {
const images = [...props.images]
if (direction === 'left' && index > 0) {
const copy = images.splice(index, 1)[0]
images.splice(index - 1, 0, copy)
} else if (direction === 'right' && index < images.length - 1) {
const copy = images.splice(index, 1)[0]
images.splice(index + 1, 0, copy)
}
props.onImagesSorted(images)
setTimeout(() => {
mainSwipeRef.current.swiper.slideTo(direction === 'left' ? index - 1 : index + 1)
}, 0)
}
const handleSaveBeforeSlideChange = () => {
handleSlideDescriptionChange(slideIndex(), 'body', slideBody())
}
onMount(async () => {
const { register } = await import('swiper/element/bundle')
register()
SwiperCore.use([Pagination, Navigation, Manipulation])
})
return (
<div class={clsx(styles.Swiper, styles.editorMode)}>
<div class={styles.container}>
<Show when={props.images.length === 0}>
<DropArea
fileType="image"
isMultiply={true}
placeholder={t('Add images')}
onUpload={handleDropAreaUpload}
description={
<div>
{t('You can upload up to 100 images in .jpg, .png format.')}
<br />
{t('Each image must be no larger than 5 MB.')}
</div>
}
/>
</Show>
<Show when={props.images.length > 0}>
<div class={styles.holder}>
<swiper-container
ref={(el) => (mainSwipeRef.current = el)}
slides-per-view={1}
thumbs-swiper={'.thumbSwiper'}
observer={true}
onSlideChange={handleSlideChange}
onBeforeSlideChangeStart={handleSaveBeforeSlideChange}
space-between={20}
>
<For each={props.images}>
{(slide, index) => (
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
<swiper-slide lazy="true" virtual-index={index()}>
<div class={styles.image}>
<Image src={slide.url} alt={slide.title} width={800} />
<Popover content={t('Delete')}>
{(triggerRef: (el) => void) => (
<div ref={triggerRef} onClick={() => handleDelete(index())} class={styles.action}>
<Icon class={styles.icon} name="delete-white" />
</div>
)}
</Popover>
</div>
</swiper-slide>
)}
</For>
</swiper-container>
<div
class={clsx(styles.navigation, styles.prev, {
[styles.disabled]: slideIndex() === 0,
})}
onClick={() => mainSwipeRef.current.swiper.slidePrev()}
>
<Icon name="swiper-l-arr" class={styles.icon} />
</div>
<div
class={clsx(styles.navigation, styles.next, {
[styles.disabled]: slideIndex() + 1 === props.images.length,
})}
onClick={() => mainSwipeRef.current.swiper.slideNext()}
>
<Icon name="swiper-r-arr" class={styles.icon} />
</div>
<div class={styles.counter}>
{slideIndex() + 1} / {props.images.length}
</div>
</div>
<div class={clsx(styles.holder, styles.thumbsHolder)}>
<div class={styles.thumbs}>
<swiper-container
class={'thumbSwiper'}
ref={(el) => (thumbSwipeRef.current = el)}
slides-per-view={'auto'}
space-between={20}
auto-scroll-offset={1}
watch-overflow={true}
watch-slides-visibility={true}
direction={'horizontal'}
slides-offset-after={160}
slides-offset-before={30}
>
<For each={props.images}>
{(slide, index) => (
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
<swiper-slide virtual-index={index()} style={{ width: 'auto', height: 'auto' }}>
<div
class={clsx(styles.imageThumb)}
style={{
'background-image': `url(${getImageUrl(slide.url, { width: 110, height: 75 })})`,
}}
>
<div class={styles.thumbAction}>
<div class={clsx(styles.action)} onClick={() => handleDelete(index())}>
<Icon class={styles.icon} name="delete-white" />
</div>
<div
class={clsx(styles.action, {
[styles.hidden]: index() === 0,
})}
onClick={() => handleChangeIndex('left', index())}
>
<Icon
class={styles.icon}
name="arrow-right-white"
style={{ transform: 'rotate(-180deg)' }}
/>
</div>
<div
class={clsx(styles.action, {
[styles.hidden]: index() === props.images.length - 1,
})}
onClick={() => handleChangeIndex('right', index())}
>
<Icon class={styles.icon} name="arrow-right-white" />
</div>
</div>
</div>
</swiper-slide>
)}
</For>
<div class={styles.upload}>
<div class={styles.inner} onClick={handleUploadThumb}>
<Show when={!loading()} fallback={<Loading size="small" />}>
<Icon name="swiper-plus" />
</Show>
</div>
</div>
</swiper-container>
<div
class={clsx(styles.navigation, styles.thumbsNav, styles.prev, {
[styles.disabled]: slideIndex() === 0,
})}
onClick={() => thumbSwipeRef.current.swiper.slidePrev()}
>
<Icon name="swiper-l-arr" class={styles.icon} />
</div>
<div
class={clsx(styles.navigation, styles.thumbsNav, styles.next, {
[styles.disabled]: slideIndex() + 1 === props.images.length,
})}
onClick={() => thumbSwipeRef.current.swiper.slideNext()}
>
<Icon name="swiper-r-arr" class={styles.icon} />
</div>
</div>
</div>
</Show>
</div>
<Show when={props.images.length > 0}>
<div class={styles.description}>
<input
type="text"
class={clsx(styles.input, styles.title)}
placeholder={t('Enter image title')}
value={props.images[slideIndex()]?.title}
onChange={(event) => handleSlideDescriptionChange(slideIndex(), 'title', event.target.value)}
/>
<input
type="text"
class={styles.input}
placeholder={t('Specify the source and the name of the author')}
value={props.images[slideIndex()]?.source}
onChange={(event) => handleSlideDescriptionChange(slideIndex(), 'source', event.target.value)}
/>
<SimplifiedEditor
initialContent={props.images[slideIndex()]?.body}
smallHeight={true}
placeholder={t('Enter image description')}
onChange={(value) => setSlideBody(value)}
/>
</div>
</Show>
</div>
)
}

View File

@ -1,59 +1,34 @@
import { createFileUploader } from '@solid-primitives/upload'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { createEffect, createSignal, For, Show, on, onMount, lazy } from 'solid-js' import { createEffect, createSignal, For, Show, on, onMount, lazy, onCleanup } from 'solid-js'
import SwiperCore, { Manipulation, Navigation, Pagination } from 'swiper' import SwiperCore, { Manipulation, Navigation, Pagination } from 'swiper'
import { throttle } from 'throttle-debounce'
import { useLocalize } from '../../../context/localize' import { MediaItem } from '../../../pages/types'
import { useSnackbar } from '../../../context/snackbar'
import { MediaItem, UploadedFile } from '../../../pages/types'
import { composeMediaItems } from '../../../utils/composeMediaItems'
import { getImageUrl } from '../../../utils/getImageUrl' import { getImageUrl } from '../../../utils/getImageUrl'
import { handleImageUpload } from '../../../utils/handleImageUpload'
import { validateFiles } from '../../../utils/validateFile'
import { DropArea } from '../DropArea'
import { Icon } from '../Icon' import { Icon } from '../Icon'
import { Image } from '../Image' import { Image } from '../Image'
import { Loading } from '../Loading'
import { Popover } from '../Popover'
import { SwiperRef } from './swiper' import { SwiperRef } from './swiper'
import styles from './Swiper.module.scss' import styles from './Swiper.module.scss'
const SimplifiedEditor = lazy(() => import('../../Editor/SimplifiedEditor'))
type Props = { type Props = {
images: MediaItem[] images: MediaItem[]
editorMode?: boolean
onImagesAdd?: (value: MediaItem[]) => void onImagesAdd?: (value: MediaItem[]) => void
onImagesSorted?: (value: MediaItem[]) => void onImagesSorted?: (value: MediaItem[]) => void
onImageDelete?: (mediaItemIndex: number) => void onImageDelete?: (mediaItemIndex: number) => void
onImageChange?: (index: number, value: MediaItem) => void onImageChange?: (index: number, value: MediaItem) => void
} }
export const ImageSwiper = (props: Props) => { const MIN_WIDTH = 540
const { t } = useLocalize()
const [loading, setLoading] = createSignal(false)
const [slideIndex, setSlideIndex] = createSignal(0)
const [slideBody, setSlideBody] = createSignal<string>()
export const ImageSwiper = (props: Props) => {
const [slideIndex, setSlideIndex] = createSignal(0)
const [isMobileView, setIsMobileView] = createSignal(false)
const mainSwipeRef: { current: SwiperRef } = { current: null } const mainSwipeRef: { current: SwiperRef } = { current: null }
const thumbSwipeRef: { current: SwiperRef } = { current: null } const thumbSwipeRef: { current: SwiperRef } = { current: null }
const swiperMainContainer: { current: HTMLDivElement } = { current: null }
const {
actions: { showSnackbar },
} = useSnackbar()
const handleSlideDescriptionChange = (index: number, field: string, value) => {
if (props.onImageChange) {
props.onImageChange(index, { ...props.images[index], [field]: value })
}
}
const swipeToUploaded = () => {
setTimeout(() => {
mainSwipeRef.current.swiper.slideTo(props.images.length - 1)
}, 0)
}
const handleSlideChange = () => { const handleSlideChange = () => {
thumbSwipeRef.current.swiper.slideTo(mainSwipeRef.current.swiper.activeIndex) thumbSwipeRef.current.swiper.slideTo(mainSwipeRef.current.swiper.activeIndex)
setSlideIndex(mainSwipeRef.current.swiper.activeIndex) setSlideIndex(mainSwipeRef.current.swiper.activeIndex)
@ -69,99 +44,41 @@ export const ImageSwiper = (props: Props) => {
{ defer: true }, { defer: true },
), ),
) )
const handleDropAreaUpload = (value: UploadedFile[]) => {
props.onImagesAdd(composeMediaItems(value))
swipeToUploaded()
}
const handleDelete = (index: number) => {
props.onImageDelete(index)
if (index === 0) {
mainSwipeRef.current.swiper.update()
} else {
mainSwipeRef.current.swiper.slideTo(index - 1)
}
}
const { selectFiles } = createFileUploader({
multiple: true,
accept: `image/*`,
})
const initUpload = async (selectedFiles) => {
const isValid = validateFiles('image', selectedFiles)
if (isValid) {
try {
setLoading(true)
const results: UploadedFile[] = []
for (const file of selectedFiles) {
const result = await handleImageUpload(file)
results.push(result)
}
props.onImagesAdd(composeMediaItems(results))
setLoading(false)
swipeToUploaded()
} catch (error) {
await showSnackbar({ type: 'error', body: t('Error') })
console.error('[runUpload]', error)
setLoading(false)
}
} else {
await showSnackbar({ type: 'error', body: t('Invalid file type') })
setLoading(false)
return false
}
}
const handleUploadThumb = async () => {
selectFiles((selectedFiles) => {
initUpload(selectedFiles)
})
}
const handleChangeIndex = (direction: 'left' | 'right', index: number) => {
const images = [...props.images]
if (direction === 'left' && index > 0) {
const copy = images.splice(index, 1)[0]
images.splice(index - 1, 0, copy)
} else if (direction === 'right' && index < images.length - 1) {
const copy = images.splice(index, 1)[0]
images.splice(index + 1, 0, copy)
}
props.onImagesSorted(images)
setTimeout(() => {
mainSwipeRef.current.swiper.slideTo(direction === 'left' ? index - 1 : index + 1)
}, 0)
}
const handleSaveBeforeSlideChange = () => {
handleSlideDescriptionChange(slideIndex(), 'body', slideBody())
}
onMount(async () => { onMount(async () => {
const { register } = await import('swiper/element/bundle') const { register } = await import('swiper/element/bundle')
register() register()
SwiperCore.use([Pagination, Navigation, Manipulation]) SwiperCore.use([Pagination, Navigation, Manipulation, ResizeObserver])
})
onMount(() => {
const updateDirection = () => {
const width = window.innerWidth
const direction = width > MIN_WIDTH ? 'vertical' : 'horizontal'
if (direction === 'horizontal') {
setIsMobileView(true)
} else {
setIsMobileView(false)
}
thumbSwipeRef.current?.swiper?.changeDirection(direction)
}
updateDirection()
const handleResize = throttle(100, () => {
updateDirection()
})
window.addEventListener('resize', handleResize)
onCleanup(() => {
window.removeEventListener('resize', handleResize)
})
}) })
return ( return (
<div class={clsx(styles.Swiper, props.editorMode ? styles.editorMode : styles.articleMode)}> <div class={clsx(styles.Swiper, styles.articleMode, { [styles.mobileView]: isMobileView() })}>
<div class={styles.container}> <div class={styles.container} ref={(el) => (swiperMainContainer.current = el)}>
<Show when={props.editorMode && props.images.length === 0}>
<DropArea
fileType="image"
isMultiply={true}
placeholder={t('Add images')}
onUpload={handleDropAreaUpload}
description={
<div>
{t('You can upload up to 100 images in .jpg, .png format.')}
<br />
{t('Each image must be no larger than 5 MB.')}
</div>
}
/>
</Show>
<Show when={props.images.length > 0}> <Show when={props.images.length > 0}>
<div class={styles.holder}> <div class={styles.holder}>
<swiper-container <swiper-container
@ -170,8 +87,7 @@ export const ImageSwiper = (props: Props) => {
thumbs-swiper={'.thumbSwiper'} thumbs-swiper={'.thumbSwiper'}
observer={true} observer={true}
onSlideChange={handleSlideChange} onSlideChange={handleSlideChange}
onBeforeSlideChangeStart={handleSaveBeforeSlideChange} space-between={isMobileView() ? 20 : 10}
space-between={20}
> >
<For each={props.images}> <For each={props.images}>
{(slide, index) => ( {(slide, index) => (
@ -180,19 +96,6 @@ export const ImageSwiper = (props: Props) => {
<swiper-slide lazy="true" virtual-index={index()}> <swiper-slide lazy="true" virtual-index={index()}>
<div class={styles.image}> <div class={styles.image}>
<Image src={slide.url} alt={slide.title} width={800} /> <Image src={slide.url} alt={slide.title} width={800} />
<Show when={props.editorMode}>
<Popover content={t('Delete')}>
{(triggerRef: (el) => void) => (
<div
ref={triggerRef}
onClick={() => handleDelete(index())}
class={styles.action}
>
<Icon class={styles.icon} name="delete-white" />
</div>
)}
</Popover>
</Show>
</div> </div>
</swiper-slide> </swiper-slide>
)} )}
@ -224,13 +127,10 @@ export const ImageSwiper = (props: Props) => {
class={'thumbSwiper'} class={'thumbSwiper'}
ref={(el) => (thumbSwipeRef.current = el)} ref={(el) => (thumbSwipeRef.current = el)}
slides-per-view={'auto'} slides-per-view={'auto'}
space-between={20} space-between={isMobileView() ? 20 : 10}
auto-scroll-offset={1} auto-scroll-offset={1}
watch-overflow={true} watch-overflow={true}
watch-slides-visibility={true} watch-slides-visibility={true}
direction={props.editorMode ? 'horizontal' : 'vertical'}
slides-offset-after={props.editorMode && 160}
slides-offset-before={props.editorMode && 30}
> >
<For each={props.images}> <For each={props.images}>
{(slide, index) => ( {(slide, index) => (
@ -242,47 +142,10 @@ export const ImageSwiper = (props: Props) => {
style={{ style={{
'background-image': `url(${getImageUrl(slide.url, { width: 110, height: 75 })})`, 'background-image': `url(${getImageUrl(slide.url, { width: 110, height: 75 })})`,
}} }}
> />
<Show when={props.editorMode}>
<div class={styles.thumbAction}>
<div class={clsx(styles.action)} onClick={() => handleDelete(index())}>
<Icon class={styles.icon} name="delete-white" />
</div>
<div
class={clsx(styles.action, {
[styles.hidden]: index() === 0,
})}
onClick={() => handleChangeIndex('left', index())}
>
<Icon
class={styles.icon}
name="arrow-right-white"
style={{ transform: 'rotate(-180deg)' }}
/>
</div>
<div
class={clsx(styles.action, {
[styles.hidden]: index() === props.images.length - 1,
})}
onClick={() => handleChangeIndex('right', index())}
>
<Icon class={styles.icon} name="arrow-right-white" />
</div>
</div>
</Show>
</div>
</swiper-slide> </swiper-slide>
)} )}
</For> </For>
<Show when={props.editorMode}>
<div class={styles.upload}>
<div class={styles.inner} onClick={handleUploadThumb}>
<Show when={!loading()} fallback={<Loading size="small" />}>
<Icon name="swiper-plus" />
</Show>
</div>
</div>
</Show>
</swiper-container> </swiper-container>
<div <div
class={clsx(styles.navigation, styles.thumbsNav, styles.prev, { class={clsx(styles.navigation, styles.thumbsNav, styles.prev, {
@ -304,47 +167,17 @@ export const ImageSwiper = (props: Props) => {
</div> </div>
</Show> </Show>
</div> </div>
<Show <div class={styles.slideDescription}>
when={props.editorMode} <Show when={props.images[slideIndex()]?.title}>
fallback={ <div class={styles.articleTitle}>{props.images[slideIndex()].title}</div>
<div class={styles.slideDescription}>
<Show when={props.images[slideIndex()]?.title}>
<div class={styles.articleTitle}>{props.images[slideIndex()].title}</div>
</Show>
<Show when={props.images[slideIndex()]?.source}>
<div class={styles.source}>{props.images[slideIndex()].source}</div>
</Show>
<Show when={props.images[slideIndex()]?.body}>
<div class={styles.body} innerHTML={props.images[slideIndex()].body} />
</Show>
</div>
}
>
<Show when={props.images.length > 0}>
<div class={styles.description}>
<input
type="text"
class={clsx(styles.input, styles.title)}
placeholder={t('Enter image title')}
value={props.images[slideIndex()]?.title}
onChange={(event) => handleSlideDescriptionChange(slideIndex(), 'title', event.target.value)}
/>
<input
type="text"
class={styles.input}
placeholder={t('Specify the source and the name of the author')}
value={props.images[slideIndex()]?.source}
onChange={(event) => handleSlideDescriptionChange(slideIndex(), 'source', event.target.value)}
/>
<SimplifiedEditor
initialContent={props.images[slideIndex()]?.body}
smallHeight={true}
placeholder={t('Enter image description')}
onChange={(value) => setSlideBody(value)}
/>
</div>
</Show> </Show>
</Show> <Show when={props.images[slideIndex()]?.source}>
<div class={styles.source}>{props.images[slideIndex()].source}</div>
</Show>
<Show when={props.images[slideIndex()]?.body}>
<div class={styles.body} innerHTML={props.images[slideIndex()].body} />
</Show>
</div>
</div> </div>
) )
} }

View File

@ -38,7 +38,6 @@
} }
.container { .container {
// max-width: 800px;
margin: auto; margin: auto;
position: relative; position: relative;
padding: 24px 0; padding: 24px 0;
@ -48,6 +47,7 @@
width: 100%; width: 100%;
.thumbsHolder { .thumbsHolder {
min-width: 110px;
width: auto; width: auto;
} }
@ -60,13 +60,6 @@
margin: 0; margin: 0;
position: relative; position: relative;
& > swiper-container {
position: absolute;
top: 52px;
bottom: 52px;
left: 0;
}
.thumbsNav { .thumbsNav {
height: 52px; height: 52px;
padding: 14px 0; padding: 14px 0;
@ -92,6 +85,48 @@
} }
} }
} }
&.mobileView {
.container {
flex-direction: column-reverse;
padding: 0;
.thumbsHolder {
min-width: unset;
}
.thumbs {
width: 100%;
height: 80px;
padding: 0;
& swiper-slide {
//bind to html element <swiper-slide/>
width: unset !important;
}
.thumbsNav {
height: 100%;
padding: 0;
width: 40px;
.icon {
transform: none;
}
&.prev {
top: 0;
left: 0;
}
&.next {
top: 0;
right: 0;
left: unset;
}
}
}
}
}
} }
&.editorMode { &.editorMode {

View File

@ -1 +1,2 @@
export { ImageSwiper } from './ImageSwiper' export { ImageSwiper } from './ImageSwiper'
export { EditorSwiper } from './EditorSwiper'