Skip to content

Commit

Permalink
feat(ImageViewer): add renderFooter prop (#5228)
Browse files Browse the repository at this point in the history
* feat(ImageViewer): add renderFooter prop

* fix(ImageViewer): footer cannot update after swipe image
  • Loading branch information
Faremax committed May 25, 2022
1 parent d7def8a commit 51ea559
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 5 deletions.
62 changes: 62 additions & 0 deletions src/components/image-viewer/demos/demo1.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,64 @@ const Multi = () => {
)
}

// 自定义footer
const ViewWithFooter = () => {
const [imageVisible, setImageVisible] = useState(false)
const [multiVisible, setMultiVisible] = useState(false)

const renderFooter = (image: string, index?: number) => {
return (
<div style={{ textAlign: 'center' }}>
<Button
color='primary'
fill='outline'
onClick={() => {
console.log('Loading...')
}}
>
查看原图{index !== undefined ? index + 1 : ''}
</Button>
</div>
)
}

return (
<>
<Button
onClick={() => {
setImageVisible(true)
}}
>
单张图片
</Button>
<ImageViewer
image={demoImage}
visible={imageVisible}
onClose={() => {
setImageVisible(false)
}}
renderFooter={renderFooter}
/>
<Button
onClick={() => {
setMultiVisible(true)
}}
>
多张图片
</Button>
<ImageViewer.Multi
images={demoImages}
visible={multiVisible}
defaultIndex={1}
onClose={() => {
setMultiVisible(false)
}}
renderFooter={renderFooter}
/>
</>
)
}

export default () => {
return (
<>
Expand Down Expand Up @@ -85,6 +143,10 @@ export default () => {
显示图片并在3秒后关闭
</Button>
</DemoBlock>

<DemoBlock title='自定义footer'>
<ViewWithFooter />
</DemoBlock>
</>
)
}
6 changes: 6 additions & 0 deletions src/components/image-viewer/image-viewer.less
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
touch-action: none;
user-select: none;
}
&-footer {
position: absolute;
width: 100%;
bottom: 0;
z-index: 1;
}
&-slides {
height: 100%;
position: relative;
Expand Down
37 changes: 32 additions & 5 deletions src/components/image-viewer/image-viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React, {
useImperativeHandle,
useRef,
useState,
useCallback,
} from 'react'

import { mergeProps } from '../../utils/with-default-props'
Expand All @@ -12,6 +13,7 @@ import {
renderToContainer,
} from '../../utils/render-to-container'
import Mask from '../mask'
import SafeArea from '../safe-area'
import { Slide } from './slide'
import { Slides, SlidesRef } from './slides'

Expand All @@ -24,6 +26,7 @@ export type ImageViewerProps = {
visible?: boolean
onClose?: () => void
afterClose?: () => void
renderFooter?: (image: string) => React.ReactNode
}

const defaultProps = {
Expand Down Expand Up @@ -53,17 +56,27 @@ export const ImageViewer: FC<ImageViewerProps> = p => {
/>
)}
</div>
{props.image && (
<div className={`${classPrefix}-footer`}>
{props.renderFooter?.(props.image)}
<SafeArea position='bottom' />
</div>
)}
</Mask>
)
return renderToContainer(props.getContainer, node)
}

export type MultiImageViewerRef = SlidesRef

export type MultiImageViewerProps = Omit<ImageViewerProps, 'image'> & {
export type MultiImageViewerProps = Omit<
ImageViewerProps,
'image' | 'renderFooter'
> & {
images?: string[]
defaultIndex?: number
onIndexChange?: (index: number) => void
renderFooter?: (image: string, index: number) => React.ReactNode
}

const multiDefaultProps = {
Expand All @@ -75,16 +88,24 @@ export const MultiImageViewer = forwardRef<
MultiImageViewerProps
>((p, ref) => {
const props = mergeProps(multiDefaultProps, p)
const [defaultIndex, setDefaultIndex] = useState(props.defaultIndex)
const [index, setIndex] = useState(props.defaultIndex)

const slidesRef = useRef<SlidesRef>(null)
useImperativeHandle(ref, () => ({
swipeTo: (index: number, immediate?: boolean) => {
setDefaultIndex(index)
setIndex(index)
slidesRef.current?.swipeTo(index, immediate)
},
}))

const onSlideChange = useCallback(
(index: number) => {
setIndex(index)
props.onIndexChange?.(index)
},
[props.onIndexChange]
)

const node = (
<Mask
visible={props.visible}
Expand All @@ -96,8 +117,8 @@ export const MultiImageViewer = forwardRef<
{props.images && (
<Slides
ref={slidesRef}
defaultIndex={defaultIndex}
onIndexChange={props.onIndexChange}
defaultIndex={index}
onIndexChange={onSlideChange}
images={props.images}
onTap={() => {
props.onClose?.()
Expand All @@ -106,6 +127,12 @@ export const MultiImageViewer = forwardRef<
/>
)}
</div>
{props.images && (
<div className={`${classPrefix}-footer`}>
{props.renderFooter?.(props.images[index], index)}
<SafeArea position='bottom' />
</div>
)}
</Mask>
)
return renderToContainer(props.getContainer, node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,130 @@ exports[`ImageViewer.Multi calling ref.current.swipeTo before initialization 2`]
</div>
</div>
</div>
<div
class="adm-image-viewer-footer"
>
<div
class="adm-safe-area adm-safe-area-position-bottom"
/>
</div>
</div>
</div>
</div>
`;

exports[`ImageViewer.Multi rendering with footer 1`] = `
<div>
<div
class="adm-mask"
style="background: rgba(0, 0, 0, 0.75); opacity: 1;"
>
<div
class="adm-mask-content"
>
<div
class="adm-image-viewer-content"
>
<div
class="adm-image-viewer-slides"
>
<div
class="adm-image-viewer-indicator"
>
1 / 4
</div>
<div
class="adm-image-viewer-slides-inner"
style="transform: none;"
>
<div
class="adm-image-viewer-slide"
>
<div
class="adm-image-viewer-control"
>
<div
class="adm-image-viewer-image-wrapper"
style="transform: none;"
>
<img
alt="https://images.unsplash.com/photo-1620476214170-1d8080f65cdb?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=3150&q=80"
draggable="false"
src="https://images.unsplash.com/photo-1620476214170-1d8080f65cdb?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=3150&q=80"
/>
</div>
</div>
</div>
<div
class="adm-image-viewer-slide"
>
<div
class="adm-image-viewer-control"
>
<div
class="adm-image-viewer-image-wrapper"
style="transform: none;"
>
<img
alt="https://images.unsplash.com/photo-1601128533718-374ffcca299b?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=3128&q=80"
draggable="false"
src="https://images.unsplash.com/photo-1601128533718-374ffcca299b?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=3128&q=80"
/>
</div>
</div>
</div>
<div
class="adm-image-viewer-slide"
>
<div
class="adm-image-viewer-control"
>
<div
class="adm-image-viewer-image-wrapper"
style="transform: none;"
>
<img
alt="https://images.unsplash.com/photo-1567945716310-4745a6b7844b?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=3113&q=80"
draggable="false"
src="https://images.unsplash.com/photo-1567945716310-4745a6b7844b?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=3113&q=80"
/>
</div>
</div>
</div>
<div
class="adm-image-viewer-slide"
>
<div
class="adm-image-viewer-control"
>
<div
class="adm-image-viewer-image-wrapper"
style="transform: none;"
>
<img
alt="https://images.unsplash.com/photo-1624993590528-4ee743c9896e?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=200&h=1000&q=80"
draggable="false"
src="https://images.unsplash.com/photo-1624993590528-4ee743c9896e?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=200&h=1000&q=80"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="adm-image-viewer-footer"
>
<button
class="adm-button adm-button-default adm-button-shape-default"
type="button"
>
查看原图
</button>
<div
class="adm-safe-area adm-safe-area-position-bottom"
/>
</div>
</div>
</div>
</div>
Expand Down
14 changes: 14 additions & 0 deletions src/components/image-viewer/tests/image-viewer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,18 @@ describe('ImageViewer.Multi', () => {
expect(renderer.getByText('3 / 4')).not.toBeNull()
expect(renderer.container).toMatchSnapshot()
})

test('rendering with footer', async () => {
function App() {
return (
<ImageViewer.Multi
images={demoImages}
visible
renderFooter={() => <Button>查看原图</Button>}
/>
)
}
const renderer = render(<App />)
expect(renderer.container).toMatchSnapshot()
})
})

0 comments on commit 51ea559

Please sign in to comment.