Răsfoiți Sursa

feat: add some icon iconrender method (#2919)

* feat: add some icon iconrender method

* feat: [Image] add render method for preview page icon

---------

Co-authored-by: ouxiaofeng.utlf <[email protected]>
Co-authored-by: zhangyumei.0319 <[email protected]>
rubbishmaker 2 luni în urmă
părinte
comite
ec0f7b370d

+ 3 - 0
content/show/image/index-en-US.md

@@ -520,6 +520,9 @@ Other attributes same as [img](https://developer.mozilla.org/en-US/docs/Web/HTML
 | previewCls        | Custom preview style class name                                                                                                                                       | string           | - | |
 | previewStyle        | Custom preview style                                                                                                                                       | object           | - | |
 | prevTip          | Previous operation button prompt                                                                                                                                         | string  | "Previous" | |
+| renderCloseIcon  | custom close icon | ReactElement | ()=>ReactElement | - | 2.85.0 |
+| renderLeftIcon   | custom left icon | ReactElement | (index)=>ReactElement | - | 2.85.0 |
+| renderRightIcon  | custom right icon | ReactElement | (index)=>ReactElement | - | 2.85.0 |
 | renderHeader     | Custom render preview top info                                                                                                                                           |(info: reactNode) => ReactNode  | - | |
 | renderPreviewMenu | Custom render preview bottom menu information                                                                                                                            | (props: MenuProps) => ReactNode; | - | |
 | rotateTip        | Tips for rotating action buttons                                                                                                                                         |string        | "Rotate" | |

+ 3 - 0
content/show/image/index.md

@@ -522,6 +522,9 @@ import { Image, ImagePreview } from '@douyinfe/semi-ui';
 | previewCls        | 自定义预览样式类名                                                                                                                                       | string           | - | |
 | previewStyle        | 自定义预览样式                                                                                                                                       | object           | - | |
 | prevTip           | 上一步操作按钮提示                                                                                                                                        | string         | "上一步" | |
+| renderCloseIcon  | 自定义关闭icon | ReactElement | () => ReactElement | - | 2.85.0 |
+| renderLeftIcon   | 自定义向左icon | ReactElement | (index) => ReactElement | - | 2.85.0 |
+| renderRightIcon  | 自定义向右icon | ReactElement | (index) => ReactElement | - | 2.85.0 |
 | renderHeader      | 自定义渲染预览顶部信息                                                                                                                                      | (info: ReactNode) => ReactNode  | - | |
 | renderPreviewMenu | 自定义渲染预览底部菜单信息                                                                                                                                    | (props: MenuProps) => ReactNode;| - | |
 | rotateTip         | 旋转操作按钮提示                                                                                                                                         | string         | "旋转" | |

+ 25 - 1
packages/semi-ui/image/_story/image.stories.jsx

@@ -21,7 +21,10 @@ import {
     IconWindowAdaptionStroked,
     IconRealSizeStroked,
     IconUploadError,
-    IconInfoCircle
+    IconInfoCircle,
+    IconEyeClosed,
+    IconLeftCircleStroked,
+    IconCenterRightStroked
 } from "@douyinfe/semi-icons";
 
 export default {
@@ -778,4 +781,25 @@ export const previewClsAndPreviewStyle = () => {
             })}
         </ImagePreview> 
     </>
+}
+
+export const RenderPreviewIcon = () => {
+    // 自定义关闭,左右切换按钮
+    return <ImagePreview
+       renderCloseIcon={<IconEyeClosed size="large" />}
+       renderLeftIcon={<IconLeftCircleStroked size="large" />}
+       renderRightIcon={<IconCenterRightStroked size="large" />}
+    >
+        {srcList1.map((src, index) => {
+            return (
+                <Image 
+                    key={index} 
+                    src={src} 
+                    width={200} 
+                    alt={`lamp${index + 1}`} 
+                    style={{ marginRight: 5 }}
+                />
+            );
+        })}
+    </ImagePreview> 
 }

+ 15 - 15
packages/semi-ui/image/image.tsx

@@ -70,7 +70,7 @@ export default class Image extends BaseComponent<ImageProps, ImageStates> {
             willUpdateStates.src = props.src;
             willUpdateStates.loadStatus = "loading";
         }
-        
+
         if (isObject(props.preview)) {
             const { visible } = props.preview;
             if (isBoolean(visible)) {
@@ -129,7 +129,7 @@ export default class Image extends BaseComponent<ImageProps, ImageStates> {
         const { placeholder } = this.props;
         return (
             placeholder ? (
-                <div className={prefixClsName}> 
+                <div className={prefixClsName}>
                     {placeholder}
                 </div>
             ) : this.renderDefaultLoading()
@@ -139,13 +139,13 @@ export default class Image extends BaseComponent<ImageProps, ImageStates> {
     renderError = () => {
         const { fallback } = this.props;
         const prefixClsName = `${prefixCls}-status`;
-        const fallbackNode = typeof fallback === "string" ? (<img style={{ width: "100%", height: "100%" }}src={fallback} alt="fallback"/>) : fallback;
+        const fallbackNode = typeof fallback === "string" ? (<img style={{ width: "100%", height: "100%" }} src={fallback} alt="fallback" />) : fallback;
         return (
             fallback ? (
                 <div className={prefixClsName}>
                     {fallbackNode}
                 </div>
-            ) :this.renderDefaultError()
+            ) : this.renderDefaultError()
         );
     }
 
@@ -167,28 +167,28 @@ export default class Image extends BaseComponent<ImageProps, ImageStates> {
 
     renderMask = () => (<div className={`${prefixCls}-mask`}>
         <div className={`${prefixCls}-mask-info`}>
-            <IconEyeOpened size="extra-large"/>
+            <IconEyeOpened size="extra-large" />
             <span className={`${prefixCls}-mask-info-text`}>{this.getLocalTextByKey("preview")}</span>
         </div>
     </div>);
 
     render() {
         const { src, loadStatus, previewVisible } = this.state;
-        const { src: picSrc, width, height, alt, style, className, crossOrigin, preview, 
+        const { src: picSrc, width, height, alt, style, className, crossOrigin, preview,
             fallback, placeholder, imageID, setDownloadName, imgCls, imgStyle,
-            ...restProps 
+            ...restProps
         } = this.props;
         const outerStyle = Object.assign({ width, height }, style);
         const outerCls = cls(prefixCls, className);
         const canPreview = loadStatus === "success" && preview && !this.isInGroup();
         const showPreviewCursor = preview && loadStatus === "success";
         const previewSrc = isObject(preview) ? ((preview as any).src ?? src) : src;
-        const previewProps = isObject(preview) && canPreview ? { 
-            ...omit(preview, ['className', 'style', 'previewCls', 'previewStyle']), 
-            className: preview?.previewCls, 
-            style: preview?.previewStyle 
-        }: {} as any;
-        return ( 
+        const previewProps = isObject(preview) && canPreview ? {
+            ...omit(preview, ['className', 'style', 'previewCls', 'previewStyle']),
+            className: preview?.previewCls,
+            style: preview?.previewStyle
+        } : {} as any;
+        return (
             <div
                 style={outerStyle}
                 className={outerCls}
@@ -213,7 +213,7 @@ export default class Image extends BaseComponent<ImageProps, ImageStates> {
                     onLoad={this.handleLoaded}
                 />
                 {loadStatus !== "success" && this.renderExtra()}
-                {canPreview && 
+                {canPreview &&
                     <PreviewInner
                         {...previewProps}
                         src={previewSrc}
@@ -225,5 +225,5 @@ export default class Image extends BaseComponent<ImageProps, ImageStates> {
                 }
             </div>
         );
-    } 
+    }
 }

+ 5 - 1
packages/semi-ui/image/interface.tsx

@@ -64,6 +64,9 @@ export interface PreviewProps extends BaseProps {
     minZoom?: number;
     previewCls?: string;
     previewStyle?: React.CSSProperties;
+    renderLeftIcon?: React.ReactElement | ((index: number) => ReactNode);
+    renderRightIcon?: React.ReactElement | ((index: number) => ReactNode);
+    renderCloseIcon?: React.ReactElement | (() => React.ReactElement);
     renderHeader?: (info: any) => ReactNode;
     renderPreviewMenu?: (props: MenuProps) => ReactNode;
     getPopupContainer?: () => HTMLElement;
@@ -129,7 +132,8 @@ export interface HeaderProps {
     title?: string;
     titleStyle?: React.CSSProperties;
     className?: string;
-    onClose?: (e: React.MouseEvent<HTMLElement>) => void
+    onClose?: (e: React.MouseEvent<HTMLElement>) => void;
+    renderCloseIcon?: React.ReactElement | (() => React.ReactElement)
 }
 
 export interface FooterProps extends SliderProps {

+ 1 - 1
packages/semi-ui/image/previewFooter.tsx

@@ -121,7 +121,7 @@ export default class Footer extends BaseComponent<FooterProps> {
             <Tooltip content={content} key={`tooltip-${key}`} zIndex={zIndex + 1}>
                 {element}
             </Tooltip>
-        ): element;
+        ) : element;
     }
 
     getLocalTextByKey = (key: string) => (

+ 8 - 2
packages/semi-ui/image/previewHeader.tsx

@@ -7,19 +7,25 @@ import { PreviewContext } from "./previewContext";
 
 const prefixCls = `${cssClasses.PREFIX}-preview-header`;
 
-const Header = forwardRef(({ onClose, titleStyle, className, renderHeader, closable }: HeaderProps, ref: React.LegacyRef<HTMLElement>) => (
+const Header = forwardRef(({ onClose, titleStyle, className, renderHeader, closable, renderCloseIcon }: HeaderProps, ref: React.LegacyRef<HTMLElement>) => (
     <PreviewContext.Consumer>
         {({ currentIndex, titles }) => {
             let title;
             if (titles && typeof currentIndex === "number") {
                 title = titles[currentIndex];
             }
+            const closeIcon = typeof renderCloseIcon === 'function' ? renderCloseIcon?.() : renderCloseIcon;
+            //warning 
+            if (typeof closeIcon !== 'undefined' && !React.isValidElement(closeIcon)) {
+                console.warn(`[Semi ImagePreview] RenderCloseIcon should be a valid react element or a function that returns a react element`);
+            }
+
             return (
                 <section ref={ref} className={cls(prefixCls, className)}>
                     <section className={`${prefixCls}-title`} style={titleStyle}>{renderHeader ? renderHeader(title) : title}</section>
                     {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
                     {closable && <section className={`${prefixCls}-close`} onMouseUp={onClose}>
-                        <IconClose />
+                        {React.isValidElement(closeIcon) ? closeIcon : <IconClose />}
                     </section>}
                 </section>
             );

+ 16 - 9
packages/semi-ui/image/previewInner.tsx

@@ -68,6 +68,7 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
         showTooltip: false,
         zoomStep: 0.1,
         infinite: false,
+        closable: true,
         closeOnEsc: true,
         lazyLoad: false,
         preLoad: true,
@@ -162,7 +163,7 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
                     headerDom && headerDom.contains(target) ||
                     footerDom && footerDom.contains(target) ||
                     leftIconDom && leftIconDom.contains(target) ||
-                    rightIconDom && rightIconDom.contains(target)  
+                    rightIconDom && rightIconDom.contains(target)
                 ) {
                     // Move in the operation area, return false
                     return false;
@@ -171,7 +172,7 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
                 return true;
             },
             changeImageZoom: (...args) => {
-                this.imageRef?.current && this.imageRef.current.foundation.changeZoom(...args)
+                this.imageRef?.current && this.imageRef.current.foundation.changeZoom(...args);
             }
         };
 
@@ -207,9 +208,9 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
         this.imageWrapRef = null;
         this.imageRef = React.createRef<PreviewImage>();
         this.headerRef = React.createRef<HTMLElement>();
-        this.footerRef= React.createRef<HTMLElement>();
-        this.leftIconRef= React.createRef<HTMLDivElement>();
-        this.rightIconRef= React.createRef<HTMLDivElement>();
+        this.footerRef = React.createRef<HTMLElement>();
+        this.leftIconRef = React.createRef<HTMLDivElement>();
+        this.rightIconRef = React.createRef<HTMLDivElement>();
     }
 
     static getDerivedStateFromProps(props: PreviewInnerProps, state: PreviewInnerStates) {
@@ -369,6 +370,9 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
             originTip,
             showTooltip,
             disableDownload,
+            renderLeftIcon,
+            renderRightIcon,
+            renderCloseIcon,
             renderPreviewMenu,
             renderHeader,
         } = this.props;
@@ -398,11 +402,14 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
         const total = imgSrc.length;
         const showPrev = total !== 1 && (infinite || currentIndex !== 0);
         const showNext = total !== 1 && (infinite || currentIndex !== total - 1);
+
+        const leftIcon = typeof renderLeftIcon === 'function' ? renderLeftIcon(currentIndex) : renderLeftIcon;
+        const rightIcon = typeof renderRightIcon === 'function' ? renderRightIcon(currentIndex) : renderRightIcon;
         return (
             visible && <Portal
                 getPopupContainer={getPopupContainer}
                 style={wrapperStyle}
-            >  
+            >
                 {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
                 <div
                     className={previewWrapperCls}
@@ -412,7 +419,7 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
                     ref={this.registryImageWrapRef}
                     onMouseMove={this.handleMouseMove}
                 >
-                    <Header ref={this.headerRef} className={cls(hideViewerCls)} onClose={this.handlePreviewClose} renderHeader={renderHeader} closable={closable}/>
+                    <Header ref={this.headerRef} className={cls(hideViewerCls)} onClose={this.handlePreviewClose} renderHeader={renderHeader} closable={closable} renderCloseIcon={renderCloseIcon} />
                     <PreviewImage
                         ref={this.imageRef}
                         src={imgSrc[currentIndex]}
@@ -433,7 +440,7 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
                             className={cls(`${previewPrefixCls}-icon`, `${previewPrefixCls}-prev`, hideViewerCls)}
                             onClick={(): void => this.handleSwitchImage("prev")}
                         >
-                            <IconArrowLeft size="large" />
+                            {React.isValidElement(leftIcon) ? leftIcon : <IconArrowLeft size="large" />}
                         </div>
                     )}
                     {showNext && (
@@ -443,7 +450,7 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
                             className={cls(`${previewPrefixCls}-icon`, `${previewPrefixCls}-next`, hideViewerCls)}
                             onClick={(): void => this.handleSwitchImage("next")}
                         >
-                            <IconArrowRight size="large" />
+                            {React.isValidElement(rightIcon) ? rightIcon : <IconArrowRight size="large" />}
                         </div>
                     )}
                     <Footer