Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

components/commmon: Gallery component improvements #1708

Merged
merged 3 commits into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 16 additions & 17 deletions src/components/client/campaign-news/CampaignNewsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { QuillStypeWrapper } from 'components/common/QuillStyleWrapper'
import { scrollToTop } from './utils/scrollToTop'
import { getArticleHeight } from './utils/getArticleHeight'

import withFullScreenSlider from 'components/common/withFullScreenSlider'
import Gallery from 'components/common/Gallery'

const PREFIX = 'CampaignNewsSection'
const classes = {
Expand Down Expand Up @@ -83,7 +83,6 @@ export default function CampaignNewsList({ articles }: Props) {
const { t, i18n } = useTranslation('news')
const INITIAL_HEIGHT_LIMIT = 400
const [isExpanded, expandContent] = useShowMoreContent()
const WithFullScreenSlider = withFullScreenSlider(Image)
return (
<>
{articles?.map((article, index: number) => {
Expand Down Expand Up @@ -158,21 +157,21 @@ export default function CampaignNewsList({ articles }: Props) {
{article.newsFiles.length > 0 && (
<>
<Grid container item gap={1} xs={'auto'} style={{ maxWidth: '100%' }}>
{images.map((file, index) => {
return (
<Grid item key={file.id}>
<WithFullScreenSlider
images={images}
src={file.src}
width={220}
height={140}
alt={file.fileName}
index={index}
style={{ objectFit: 'scale-down' }}
/>
</Grid>
)
})}
<Gallery images={images}>
{images.map((file) => {
return (
<Grid item key={file.id}>
<Image
src={file.src}
width={220}
height={140}
alt={file.fileName}
style={{ objectFit: 'scale-down' }}
/>
</Grid>
)
})}
</Gallery>
</Grid>
</>
)}
Expand Down
4 changes: 2 additions & 2 deletions src/components/client/campaigns/CampaignDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import SecurityIcon from '@mui/icons-material/Security'
import { styled } from '@mui/material/styles'

import DonationWishes from './DonationWishes'
import CampaignImageSlider from 'components/common/CampaignImageSlider'
import { ImageSlider } from 'components/common/ImageSlider'
import CampaignInfo from './CampaignInfo/CampaignInfo'
import CampaignInfoGraphics from './CampaignInfoGraphics'
import CampaignInfoOperator from './CampaignInfoOperator'
Expand Down Expand Up @@ -158,7 +158,7 @@ export default function CampaignDetails({ campaign }: Props) {
<ReactQuill readOnly theme="bubble" value={campaign.description} />
</Grid>
<Grid item xs={12}>
<CampaignImageSlider sliderImages={sliderImages} />
<ImageSlider sliderImages={sliderImages} />
</Grid>
<Grid item xs={12}>
<Divider />
Expand Down
32 changes: 16 additions & 16 deletions src/components/client/campaigns/CampaignNewsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { sanitizeHTML } from 'common/util/htmlUtils'
import { QuillStypeWrapper } from 'components/common/QuillStyleWrapper'
import { scrollToTop } from '../campaign-news/utils/scrollToTop'
import { getArticleHeight } from '../campaign-news/utils/getArticleHeight'
import withFullScreenSlider from 'components/common/withFullScreenSlider'
import Gallery from 'components/common/Gallery'

const PREFIX = 'NewsTimeline'

Expand Down Expand Up @@ -161,7 +161,6 @@ type Props = {
export default function CampaignNewsSection({ campaign, canCreateArticle }: Props) {
const { t, i18n } = useTranslation('news')
const { small }: { small: boolean } = useMobile()
const WithFullScreenSlider = withFullScreenSlider(Image)

const INITIAL_HEIGHT_LIMIT = 200
const [isExpanded, expandContent] = useShowMoreContent()
Expand Down Expand Up @@ -299,20 +298,21 @@ export default function CampaignNewsSection({ campaign, canCreateArticle }: Prop
))}
</Grid>
<Grid container item gap={1}>
{images.map((file, index) => {
return (
<Grid item key={file.id}>
<WithFullScreenSlider
images={images}
src={file.src}
width={164}
height={120}
alt={file.id}
index={index}
/>
</Grid>
)
})}
<Gallery images={images}>
{images.map((file) => {
return (
<Grid item key={file.id}>
<Image
src={file.src}
width={220}
height={140}
alt={file.fileName}
style={{ objectFit: 'scale-down' }}
/>
</Grid>
)
})}
</Gallery>
</Grid>
</Grid>
)}
Expand Down
76 changes: 76 additions & 0 deletions src/components/common/Gallery.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React, { useMemo, useRef, useState } from 'react'
import { FullScreenImageSlider } from './ImageSlider'
import { ImageSlider } from 'components/common/campaign-file/roles'

type ChildComponentProps = {
children: JSX.Element[] | JSX.Element
images: ImageSlider[]
}

/**
* Gallery component which allows images to expand whenever clicked.
* @param {React.JSX.Element | React.JSX.Element[]} children Single React Element , or array of React Elements.
* @param {ImageSlider[]} images List of images to show when gallery is expanded
* @returns
*/
export default function Gallery({ children, images }: ChildComponentProps) {
const initialImage = useRef<number>(0)
const [toggleFullScreeSlider, setToggleFullScreenSlider] = useState(false)

const onOpenFullScreenSlider = (index: number) => {
setToggleFullScreenSlider(true)
initialImage.current = index
}

const onCloseFullScreenSlider = () => {
setToggleFullScreenSlider(false)
initialImage.current = 0
}

const NestedChild = useMemo(() => {
const childrenCount = React.Children.count(children)

//if childrenCount > 1, assume list of thumbnail images are shown
if (childrenCount > 1) {
return React.Children.map(children, (child, index) => {
return React.cloneElement(child, {
onClick: () => onOpenFullScreenSlider(index),
style: { cursor: 'pointer' },
})
})
}

//if childrenCount === 1, assume the child is a component such as slider or gallery grid, and the list of images are nested.
return React.Children.map(children, (child) => {
//In some situations the child could be a single JSX.Element containing an image, thus child.props.children is an object rather than array
if (images.length === 1) {
return React.cloneElement(child, {
style: { cursor: 'pointer' },
onClick: () => onOpenFullScreenSlider(0),
})
}

const modifiedNestedChild = child.props.children.map((elem: JSX.Element, index: number) => {
return React.cloneElement(elem, {
onClick: () => onOpenFullScreenSlider(index),
})
})

return React.cloneElement(child, { style: { cursor: 'pointer' } }, modifiedNestedChild)
})
}, [])

return (
<>
{NestedChild}
{toggleFullScreeSlider && (
<FullScreenImageSlider
sliderImages={images}
onOpen={toggleFullScreeSlider}
onClose={onCloseFullScreenSlider}
initialImage={initialImage}
/>
)}
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ import 'slick-carousel/slick/slick.css'
import 'slick-carousel/slick/slick-theme.css'
import Image from 'next/image'
import { styled } from '@mui/material/styles'
import { Modal, Typography } from '@mui/material'
import { IconButton, Modal, Typography } from '@mui/material'
import { useTranslation } from 'next-i18next'
import theme from 'common/theme'
import { ImageSlider } from 'components/common/campaign-file/roles'
import withFullScreenSlider from './withFullScreenSlider'
import Gallery from './Gallery'
import FullscreenExitIcon from '@mui/icons-material/FullscreenExit'

const PREFIX = 'ImageSlider'
const classes = {
container: 'container',
slider: 'slider',
carouselFullScreen: 'carouselFullScreen',
container: `${PREFIX}-container`,
slider: `${PREFIX}-slider`,
carouselFullScreen: `${PREFIX}-fullScreen`,
minimizeFullScreenBtn: `${PREFIX}-minimizeFullScreenBtn`,
}

const Root = styled('div')(() => ({
Expand All @@ -30,7 +33,7 @@ const Root = styled('div')(() => ({
zIndex: 3,
},
'& .slick-next': {
right: theme.spacing(3.5),
right: theme.spacing(4),
zIndex: 3,
},
'& .slick-next::before, .slick-prev::before': {
Expand All @@ -49,19 +52,16 @@ const Root = styled('div')(() => ({
},
},
[`& .${classes.carouselFullScreen}`]: {
maxWidth: '100%',
maxHeight: '100%',
width: '100vw',
'& .slick-prev': {
left: theme.spacing(1),
zIndex: 3,
left: '5vw',
},
'& .slick-slide': {
position: 'relative',
},

'& .slick-next': {
right: theme.spacing(3.5),
zIndex: 3,
right: '5vw',
},

'& .slick-track img': {
Expand All @@ -70,18 +70,15 @@ const Root = styled('div')(() => ({
},

'& .slick-track': {
aspectRatio: 20,
minHeight: 350,
height: '42vh',
[theme.breakpoints.up(600)]: {
aspectRatio: 45,
height: '80vh',
},
[theme.breakpoints.up(800)]: {
minHeight: 370,
aspectRatio: 49,
height: '88vh',
},
[theme.breakpoints.up(1024)]: {
minHeight: 540,
aspectRatio: 34,
height: '90vh',
},
},

Expand All @@ -100,6 +97,18 @@ const Root = styled('div')(() => ({
color: theme.palette.primary.main,
},
},
[`& .${classes.minimizeFullScreenBtn}`]: {
position: 'absolute',
color: 'white',
right: 0,
top: 0,
cursor: 'pointer',
zIndex: 9999,

['svg']: {
fontSize: 50,
},
},
}))

type Props = {
Expand All @@ -123,9 +132,8 @@ const settings: Settings = {
],
}

export default function CampaignImageSlider({ sliderImages }: Props) {
export function ImageSlider({ sliderImages }: Props) {
const { t } = useTranslation()
const WithFullScreenSlider = withFullScreenSlider(Image)
if (sliderImages.length === 0) {
return null
}
Expand All @@ -150,21 +158,20 @@ export default function CampaignImageSlider({ sliderImages }: Props) {
<Typography variant="h4" sx={{ my: theme.spacing(3), fontWeight: '500' }}>
{t('campaigns:campaign.gallery')}
</Typography>
<Slider {...settings} className={classes.slider}>
{sliderImages.map((image, index) => (
<div key={index}>
<WithFullScreenSlider
images={sliderImages}
<Gallery images={sliderImages}>
<Slider {...settings} className={classes.slider}>
{sliderImages.map((image, index) => (
<Image
src={image.src}
alt={image.fileName}
height={300}
width={500}
style={{ objectFit: 'contain' }}
index={index}
key={index}
/>
</div>
))}
</Slider>
))}
</Slider>
</Gallery>
</Root>
)
}
Expand Down Expand Up @@ -195,14 +202,25 @@ export const FullScreenImageSlider = ({
return (
<Modal
open={onOpen}
hideBackdrop
onClose={onClose}
sx={{
width: '100vw',
height: '100vh',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0,0,0,0.8)',
backgroundColor: 'rgba(0,0,0,0.9)',
cursor: 'grab',
}}>
<Root style={{ width: 1000, maxWidth: '100%' }}>
<Root>
<IconButton
size="large"
onClick={onClose}
className={classes.minimizeFullScreenBtn}
aria-label="minimize gallery">
<FullscreenExitIcon />
</IconButton>
<Slider {...newSettings} className={classes.carouselFullScreen} ref={sliderRef}>
{sliderImages.map((image, index) => (
<div key={index} style={{ position: 'relative', maxWidth: '100%' }}>
Expand Down
Loading
Loading