MinJieLiu пре 7 година
родитељ
комит
e6ab2327d7

+ 1 - 1
examples/simple.tsx

@@ -42,7 +42,7 @@ class Example extends React.Component {
         <PhotoProvider>
           <ImageList>
             {photoImages.map((item, index) => (
-              <PhotoConsumer key={index} src={item}>
+              <PhotoConsumer key={index} src={item} intro={item}>
                 {index < 2 ? <SmallImage /> : undefined}
               </PhotoConsumer>
             ))}

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "react-photo-view",
-  "version": "0.1.9",
+  "version": "0.2.0",
   "description": "React photo preview.",
   "main": "./lib/index",
   "module": "./es/index",

+ 9 - 5
src/PhotoConsumer.tsx

@@ -8,6 +8,7 @@ import PhotoContext, {
 
 interface IPhotoViewItem {
   src: string;
+  intro?: React.ReactNode;
   children?: React.ReactElement<any>;
   onShow: onShowType;
   addItem: addItemType;
@@ -15,21 +16,21 @@ interface IPhotoViewItem {
 }
 
 class PhotoViewItem extends React.Component<IPhotoViewItem> {
-  private dataKey: string = uniqueId();
+  private key: string = uniqueId();
 
   componentDidMount() {
-    const { src, addItem } = this.props;
-    addItem(this.dataKey, src);
+    const { addItem, src, intro } = this.props;
+    addItem(this.key, src, intro);
   }
 
   componentWillUnmount() {
     const { removeItem } = this.props;
-    removeItem(this.dataKey);
+    removeItem(this.key);
   }
 
   handleShow = (e) => {
     const { onShow, children } = this.props;
-    onShow(this.dataKey);
+    onShow(this.key);
 
     if (children) {
       const { onClick } = children.props;
@@ -56,11 +57,13 @@ class PhotoViewItem extends React.Component<IPhotoViewItem> {
 
 export interface IPhotoConsumer {
   src: string;
+  intro?: React.ReactNode;
   children?: React.ReactElement<any>;
 }
 
 const PhotoConsumer: React.SFC<IPhotoConsumer> = ({
   src,
+  intro,
   children,
   ...restProps
 }) => (
@@ -70,6 +73,7 @@ const PhotoConsumer: React.SFC<IPhotoConsumer> = ({
         {...value}
         {...restProps}
         src={src}
+        intro={intro}
       >
         {children}
       </PhotoViewItem>

+ 20 - 41
src/PhotoProvider.tsx

@@ -5,26 +5,14 @@ import PhotoContext, {
   removeItemType,
 } from './photo-context';
 import PhotoSlider from './PhotoSlider';
-import { dataType } from './types';
+import { dataType, IPhotoProviderBase } from './types';
 
-interface IPhotoProvider {
+interface IPhotoProvider extends IPhotoProviderBase {
   children: React.ReactNode;
-  // className
-  className?: string;
-  // 遮罩 className
-  maskClassName?: string;
-  // 图片容器 className
-  viewClassName?: string;
-  // 图片 className
-  imageClassName?: string;
-  // 自定义 loading
-  loadingElement?: JSX.Element;
-  // 加载失败 Element
-  brokenElement?: JSX.Element;
 }
 
 type PhotoProviderState = {
-  data: dataType[];
+  images: dataType[];
   visible: boolean;
   index: number;
   onShow: onShowType;
@@ -40,7 +28,7 @@ export default class PhotoProvider extends React.Component<
     super(props);
 
     this.state = {
-      data: [],
+      images: [],
       visible: false,
       index: 0,
       addItem: this.handleAddItem,
@@ -49,31 +37,32 @@ export default class PhotoProvider extends React.Component<
     };
   }
 
-  handleAddItem = (dataKey: string, src: string) => {
+  handleAddItem = (key: string, src: string, intro: React.ReactNode) => {
     this.setState(prev => ({
-      data: prev.data.concat({
-        dataKey,
+      images: prev.images.concat({
+        key,
         src,
+        intro,
       }),
     }));
   }
 
-  handleRemoveItem = (dataKey: string) => {
-    this.setState(({ data, index }) => {
-      const nextData = data.filter(item => item.dataKey !== dataKey);
-      const nextEndIndex = nextData.length - 1;
+  handleRemoveItem = (key: string) => {
+    this.setState(({ images, index }) => {
+      const nextImages = images.filter(item => item.key !== key);
+      const nextEndIndex = nextImages.length - 1;
       return {
-        data: nextData,
+        images: nextImages,
         index: Math.min(nextEndIndex, index),
       };
     });
   }
 
-  handleShow = (dataKey: string) => {
-    const { data } = this.state;
+  handleShow = (key: string) => {
+    const { images } = this.state;
     this.setState({
       visible: true,
-      index: data.findIndex(item => item.dataKey === dataKey),
+      index: images.findIndex(item => item.key === key),
     });
   }
 
@@ -91,31 +80,21 @@ export default class PhotoProvider extends React.Component<
 
   render() {
     const {
-      className,
-      maskClassName,
-      viewClassName,
-      imageClassName,
-      loadingElement,
-      brokenElement,
       children,
+      ...restProps
     } = this.props;
-    const { data, visible, index } = this.state;
+    const { images, visible, index } = this.state;
 
     return (
       <PhotoContext.Provider value={this.state}>
         {children}
         <PhotoSlider
-          images={data}
+          images={images}
           visible={visible}
           index={index}
           onIndexChange={this.handleIndexChange}
           onClose={this.handleClose}
-          className={className}
-          maskClassName={maskClassName}
-          viewClassName={viewClassName}
-          imageClassName={imageClassName}
-          loadingElement={loadingElement}
-          brokenElement={brokenElement}
+          {...restProps}
         />
       </PhotoContext.Provider>
     );

+ 26 - 32
src/PhotoSlider.tsx

@@ -2,13 +2,14 @@ import React from 'react';
 import PhotoView from './PhotoView';
 import SlideWrap from './components/SlideWrap';
 import Backdrop from './components/Backdrop';
-import { Close, Counter, TopBar } from './components/TopWrap';
-import { dataType } from './types';
+import { Close, Counter, BannerWrap, BannerRight } from './components/BannerWrap';
+import FooterWrap from './components/FooterWrap';
+import { dataType, IPhotoProviderBase } from './types';
 import { defaultOpacity, horizontalOffset, maxMoveOffset } from './variables';
 
-interface IPhotoSliderProps {
+interface IPhotoSliderProps extends IPhotoProviderBase {
   // 图片列表
-  images: (string | dataType)[];
+  images: dataType[];
   // 图片当前索引
   index?: number;
   // 可见
@@ -17,22 +18,6 @@ interface IPhotoSliderProps {
   onClose: (evt?: React.MouseEvent) => void;
   // 索引改变回调
   onIndexChange?: Function;
-  // 背景可点击关闭,默认 true
-  maskClosable?: boolean;
-  // 自定义容器
-  overlay?: React.ReactNode;
-  // className
-  className?: string;
-  // 遮罩 className
-  maskClassName?: string;
-  // 图片容器 className
-  viewClassName?: string;
-  // 图片 className
-  imageClassName?: string;
-  // 自定义 loading
-  loadingElement?: JSX.Element;
-  // 加载失败 Element
-  brokenElement?: JSX.Element;
 }
 
 type PhotoSliderState = {
@@ -63,6 +48,8 @@ export default class PhotoSlider extends React.Component<
 
   static defaultProps = {
     maskClosable: true,
+    bannerVisible: true,
+    introVisible: true,
   };
 
   static getDerivedStateFromProps(nextProps, prevState) {
@@ -235,6 +222,8 @@ export default class PhotoSlider extends React.Component<
       viewClassName,
       imageClassName,
       onClose,
+      bannerVisible,
+      introVisible,
       loadingElement,
       brokenElement,
     } = this.props;
@@ -248,6 +237,9 @@ export default class PhotoSlider extends React.Component<
     } = this.state;
     const imageLength = images.length;
     const transform = `translate3d(${translateX}px, 0px, 0)`;
+    // Overlay
+    const overlayIntro = imageLength ? images[photoIndex].intro : undefined;
+    const overlayStyle = { opacity: +overlayVisible };
 
     if (visible) {
       const { innerWidth } = window;
@@ -258,29 +250,28 @@ export default class PhotoSlider extends React.Component<
             className={maskClassName}
             style={{ background: `rgba(0, 0, 0, ${backdropOpacity})` }}
           />
-          <TopBar style={{ opacity: +overlayVisible }}>
-            <Counter>{photoIndex + 1} / {imageLength}</Counter>
-            <Close onClick={onClose} />
-          </TopBar>
+          {bannerVisible ? (
+            <BannerWrap style={overlayStyle}>
+              <Counter>{photoIndex + 1} / {imageLength}</Counter>
+              <BannerRight>
+                <Close onClick={onClose} />
+              </BannerRight>
+            </BannerWrap>
+          ) : undefined}
           {images
             .slice( // 加载相邻三张
               Math.max(photoIndex - 1, 0),
               Math.min(photoIndex + 2, imageLength + 1)
             )
-            .map((item: string | dataType, index) => {
-              const isStrItem = typeof item === 'string';
+            .map((item: dataType, index) => {
               // 截取之前的索引位置
               const realIndex = photoIndex === 0
                 ? photoIndex + index
                 : photoIndex - 1 + index;
               return (
                 <PhotoView
-                  key={
-                    isStrItem
-                      ? (item as string) + realIndex
-                      : (item as dataType).dataKey
-                  }
-                  src={isStrItem ? (item as string) : (item as dataType).src}
+                  key={item.key || realIndex}
+                  src={item.src}
                   onReachTopMove={this.handleReachVerticalMove}
                   onReachBottomMove={this.handleReachVerticalMove}
                   onReachRightMove={
@@ -310,6 +301,9 @@ export default class PhotoSlider extends React.Component<
                 />
               );
             })}
+          {introVisible && overlayIntro ? (
+            <FooterWrap style={overlayStyle}>{overlayIntro}</FooterWrap>
+          ) : undefined}
           {overlay}
         </SlideWrap>
       );

+ 1 - 0
src/PhotoView.tsx

@@ -94,6 +94,7 @@ export default class PhotoView extends React.Component<
 
   constructor(props) {
     super(props);
+    // 加入延迟触发,避免与双击混淆
     this.photoClick = debounce(this.photoClick, 300);
     this.handleMove = throttle(this.handleMove, 8);
   }

+ 5 - 2
src/components/TopWrap.tsx → src/components/BannerWrap.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import styled from 'styled-components';
 
-export const TopBar = styled.div`
+export const BannerWrap = styled.div`
   position: absolute;
   left: 0;
   top: 0;
@@ -13,7 +13,6 @@ export const TopBar = styled.div`
   color: white;
   background-color: rgba(0, 0, 0, 0.5);
   transition: opacity 0.2s ease-out;
-  will-change: opacity;
   z-index: 20;
 `;
 
@@ -23,6 +22,10 @@ export const Counter = styled.div`
   opacity: 0.75;
 `;
 
+export const BannerRight = styled.div`
+  height: 100%;
+`;
+
 function CloseSVG(props) {
   return (
     <svg

+ 20 - 0
src/components/FooterWrap.tsx

@@ -0,0 +1,20 @@
+import React from 'react';
+import styled from 'styled-components';
+
+const FooterWrap = styled.div`
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  padding: 10px;
+  width: 100%;
+  min-height: 44px;
+  line-height: 1.5;
+  font-size: 14px;
+  color: #ccc;
+  background-color: rgba(0, 0, 0, 0.5);
+  text-align: justify;
+  transition: opacity 0.2s ease-out;
+  z-index: 20;
+`;
+
+export default FooterWrap;

+ 7 - 3
src/photo-context.ts

@@ -1,10 +1,14 @@
 import React from 'react';
 
-export type onShowType = (dataKey?: string) => void;
+export type onShowType = (key?: string) => void;
 
-export type addItemType = (dataKey?: string, src?: string) => void;
+export type addItemType = (
+  key?: string,
+  src?: string,
+  intro?: React.ReactNode,
+) => void;
 
-export type removeItemType = (dataKey?: string) => void;
+export type removeItemType = (key?: string) => void;
 
 export default React.createContext({
   onShow() {},

+ 30 - 1
src/types.ts

@@ -1,11 +1,40 @@
 /**
  * 图片 item 类型
  */
+import React from 'react';
+
 export type dataType = {
-  dataKey: string;
+  // 唯一标识
+  key?: string;
+  // 图片地址
   src: string;
+  // 图片介绍
+  intro?: React.ReactNode;
 };
 
+export interface IPhotoProviderBase {
+  // 背景可点击关闭,默认 true
+  maskClosable?: boolean;
+  // 导航条 visible,默认 true
+  bannerVisible?: boolean;
+  // 简介 visible,默认 true
+  introVisible?: boolean;
+  // 自定义容器
+  overlay?: React.ReactNode;
+  // className
+  className?: string;
+  // 遮罩 className
+  maskClassName?: string;
+  // 图片容器 className
+  viewClassName?: string;
+  // 图片 className
+  imageClassName?: string;
+  // 自定义 loading
+  loadingElement?: JSX.Element;
+  // 加载失败 Element
+  brokenElement?: JSX.Element;
+}
+
 export type ReachFunction = (clientX: number, clientY: number) => void;
 
 export type PhotoClickFunction = (clientX: number, clientY: number) => void;