函数式编程 调用组件 eg: modal

  • Post author:
  • Post category:其他


组件实例:
##  ModalInstance.tsx
/**
 * @description 弹窗实例
 * @param {boolean} visible - 控制弹窗显示
 * @param {string} className - 弹窗额外的类名
 * @param {string} closeClassName - 弹窗关闭按钮类名
 * @param {number} width - 弹窗宽度(默认280px)
 * @param {string} cancelText - 取消按钮文字(默认为:取消)
 * @param {string} okText - 确认按钮文字(默认为:确认)
 * @param {boolean} showFooter - 是否显示弹窗底部按钮区域(默认为:true)
 * @param {boolean} cancelable - 是否显示取消按钮(默认显示)
 * @param {function} onOk - 点击确认按钮后的回调
 * @param {function} onCancel - 点击取消按钮后的回调
 * @param {function} afterClose - 弹窗关闭后的回调
 * @param {boolean} maskClosable - 点击遮罩是否可以关闭弹窗
 * @param {string} position - 弹窗位置,两组值:center、bottom
 */
import React, { useEffect, ReactNode } from 'react';
import ReactDOM from 'react-dom';
import classNames from 'classnames';
import './Modal.scss';  // 文件在下面

function Modal (props: ModalProps):React.ReactPortal | null {
	const {
        visible,
        className,
        closeClassName,
        children,
        width = 280,
        cancelText = '取消',
        okText = '确认',
        showFooter = false,
        cancelable = true,
        maskClosable = false,
        position = 'center',
        onOk = () => {},
        onCancel = () => {},
        afterClose = () => {},
        close = onCancel,
    } = props;

 const handleModalClick = (ev) => {
        ev.stopPropagation();
  };
   
  useEffect(() => {
        if (!visible) {
            afterClose();
        }
   }, [visible]);

    if (!visible) {
        return null;
    }

 return ReactDOM.createPortal(
        <div className="CommonModal" onClick={handleModalClick}>
            <div className="CommonModal-mask" onClick={maskClosable ? close : () => {}} />
            <div
                className={classNames('CommonModal-modal', `is-${position}`, className)}
                style={{
                    width: typeof width === 'number' ? `${width}px` : width,
                }}
            >
                <i className={classNames('CommonModal-close', closeClassName)} onClick={close} />
                <div>{children}</div>
                {!!showFooter && (
                    <div className="CommonModal-footer">
                        {cancelable && (
                            <button className="CommonModal-btn CommonModal-btn--cancel" type="button" onClick={onCancel}>
                                {cancelText}
                            </button>
                        )}
                        <button className="CommonModal-btn CommonModal-btn--comfirm" type="button" onClick={onOk}>
                            {okText}
                        </button>
                    </div>
                )}
            </div>
        </div>,
        document.body,
    );
}

export default Modal;



Modal.tsx

import { createRoot } from 'react-dom/client';
import ModalInstance, { ModalProps } from './ModalInstance';
type ConfigUpdate = ModalProps | ((prevConfig: ModalProps) => ModalProps);

export interfave ModalInfoInstance {
	destroy: () => void;
	update: (configUpdate: ConfigUpdate) => viod;
}

type ModalFunc = (props: ModalProps) => ModalInfoInstance;

type ModalStaticFunctions = Record<NonNullable<ModalProps['type']>, ModalFunc>;

type ModalType = typeof ModalInstance & ModalStaticFunctions

const Modal = ModalInstance as ModalType;

const ModalInfo = (props = {} as ModalProps) => <Modal {...props}>{props.content}</Modal>;

Modal.info = (props: ModalProps) => {
	const div = document.createElement('div');
	document.body.appendChild(div);
	const root = createRoot(div)
	let config = {} as ModalProps;
		
	const render = (renderProps: ModalProps) => {
		root.render(<ModalInfo {...renderProps} />);
	};
	
	const destory = () => {
		 root.unmount();

        // 销毁后,移除元素
        if (div.parentNode) {
            div.parentNode.removeChild(div);
        }
	}

 const update = (newConfig: ConfigUpdate) => {
        config = {
            ...config,
            ...newConfig,
        };

        render(config);
    };

  const close = () => {
        config = {
            ...config,
            visible: false,
            afterClose: destroy,
        };

        render(config);
   };

	config = {
		...props,
		 onOk: props.onOk || close,
       	 onCancel: props.onCancel || close,
         close: props.close || props.onCancel || close,
         visible: true,
	};
	render (config);
	
	return {
		destroy: close,
		update,
	}
};

export default Modal;



Modal.scss

@keyframes normalModalAnimate {
    0% {
        opacity: 0;
    }

    100% {
        opacity: 1;
    }
}

@keyframes bottomModalAnimate {
    0% {
        opacity: .5;
        transform: translateY(100%);
    }

    100% {
        opacity: 1;
        transform: translateY(0);
    }
}

.CommonModal {
  .text-elipics {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  &-mask {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 2999;
  }

  &-modal {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    box-sizing: border-box;
    z-index: 2999;

    &:not(.is-bottom) {
        animation: normalModalAnimate .3s;
    }

    &.is-bottom {
        top: auto;
        left: 0;
        bottom: 0;
        transform: none;
        animation: bottomModalAnimate .5s;
    }
  }

  &-close {
    position: absolute;
    cursor: pointer;
  }

  &-footer {
    text-align: center;
  }

  &-btn {
    display: inline-block;
    width: 102px;
    height: 36px;
    margin: 0 4px;
    font-size: 13px;
    color: #FFF7C5;
    font-weight: 600;
    text-align: center;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 100% auto;
    vertical-align: top;
    cursor: pointer;

    &--comfirm {
        background-image: url(src/assets/modal-btn-confirm.png);
    }

    &--cancel {
        background-image: url(src/assets/modal-btn-cancel.png);
    }
  }
}



版权声明:本文为qq_42359718原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。