import { createEffect, createSignal, For, Match, Show, Switch, on } from 'solid-js' import { MediaItem, UploadedFile } from '../../../pages/types' import { Icon } from '../Icon' import { Popover } from '../Popover' import { useLocalize } from '../../../context/localize' import { register } from 'swiper/element/bundle' import { DropArea } from '../DropArea' import MD from '../../Article/MD' import { createFileUploader } from '@solid-primitives/upload' import SwiperCore, { Manipulation, Navigation, Pagination } from 'swiper' import { SwiperRef } from './swiper' import { validateFiles } from '../../../utils/validateFile' import { handleFileUpload } from '../../../utils/handleFileUpload' import { useSnackbar } from '../../../context/snackbar' import { Loading } from '../Loading' import { imageProxy } from '../../../utils/imageProxy' import { clsx } from 'clsx' import styles from './Swiper.module.scss' import { composeMediaItems } from '../../../utils/composeMediaItems' import SimplifiedEditor from '../../Editor/SimplifiedEditor' type Props = { images: MediaItem[] editorMode?: boolean onImagesAdd?: (value: MediaItem[]) => void onImagesSorted?: (value: MediaItem[]) => void onImageDelete?: (mediaItemIndex: number) => void onImageChange?: (index: number, value: MediaItem) => void } register() SwiperCore.use([Pagination, Navigation, Manipulation]) export const SolidSwiper = (props: Props) => { const { t } = useLocalize() const [loading, setLoading] = createSignal(false) const [slideIndex, setSlideIndex] = createSignal(0) const mainSwipeRef: { current: SwiperRef } = { current: null } const thumbSwipeRef: { current: SwiperRef } = { current: null } const { actions: { showSnackbar } } = useSnackbar() const handleSlideDescriptionChange = (index: number, field: string, value) => { 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() } ) ) 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 handleFileUpload(file) results.push(result.url) } props.onImagesAdd(composeMediaItems(results)) setLoading(false) swipeToUploaded() } catch (error) { await showSnackbar({ type: 'error', body: t('Error') }) console.error('[runUpload]', error) } } else { await showSnackbar({ type: 'error', body: t('Invalid file type') }) 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) } return (
{t('You can upload up to 100 images in .jpg, .png format.')}
{t('Each image must be no larger than 5 MB.')}
} /> 0}>
(mainSwipeRef.current = el)} slides-per-view={1} thumbs-swiper={'.thumbSwiper'} observer={true} onSlideChange={handleSlideChange} space-between={20} > {(slide, index) => ( // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore
{slide.title} {(triggerRef: (el) => void) => (
handleDelete(index())} class={styles.action} >
)}
handleSlideDescriptionChange(index(), 'title', event.target.value) } /> handleSlideDescriptionChange(index(), 'source', event.target.value) } /> handleSlideDescriptionChange(index(), 'body', value)} submitButtonText={t('Save')} />
{slide.title}
{slide.source}
)}
mainSwipeRef.current.swiper.slidePrev()} >
mainSwipeRef.current.swiper.slideNext()} >
{slideIndex() + 1} / {props.images.length}
(thumbSwipeRef.current = el)} slides-per-view={'auto'} free-mode={true} observer={true} space-between={20} auto-scroll-offset={1} watch-overflow={true} slide-to-clicked-slide={true} watch-slides-visibility={true} watch-slides-progress={true} direction={props.editorMode ? 'horizontal' : 'vertical'} slides-offset-after={props.editorMode && 140} > {(slide, index) => ( // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore
handleDelete(index())}>
handleChangeIndex('left', index())} >
handleChangeIndex('right', index())} >
)}
}>
thumbSwipeRef.current.swiper.slidePrev()} >
thumbSwipeRef.current.swiper.slideNext()} >
) }