Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | 9x 9x 9x 9x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 9x 9x 9x | import React, { useEffect, ReactNode, useRef } from "react"; import { createPortal } from "react-dom"; import classNames from "classnames"; import { StyledProps } from "../_type"; import { Button } from "../button"; import { FadeTransition, SlideTransition } from "../transition"; import { createRocket } from "../_util/create-rocket"; import { getOverlayRoot } from "../_util/get-overlay-root"; import { useVisibleTransition } from "../_util/use-visible-transition"; import { callBoth } from "../_util/call-both"; import { ModalMessage } from "./ModalMessage"; import { useOutsideClick } from "../_util/use-outside-click"; import { useConfig } from "../_util/config-context"; /** * 对话框组件配置: */ export interface ModalProps extends StyledProps { /** * 对话框内容 */ children?: ReactNode; /** * 对话框是否可见 */ visible?: boolean; /** * 对话框的标题 * 如果禁用关闭图标的同时,没有传入对话框标题,则不渲染标题 */ caption?: string | JSX.Element; /** * 对话框尺寸,决定对话框的宽度 * - `"s"` 小尺寸对话框,宽度 480px * - `"m"` 默认尺寸对话框,宽度 550px * - `"l"` 大尺寸对话框,宽度 800px * - `"xl"` 超大尺寸对话框,宽度 950px * - `"auto"` 自动适应宽度 * - 传入数字/百分比可以自定义宽度 */ size?: "s" | "m" | "l" | "xl" | "auto" | number | string; /** * 对话框关闭按钮点击时回掉 */ onClose?: () => void; /** * 对话框关闭动画结束时回调 */ onExited?: () => void; /** * 默认 ESC 键会触发 `onClose` 的调用,设置为 `true` 禁止该行为 * @default false */ disableEscape?: boolean; /** * 是否禁用关闭图标 * 如果禁用关闭图标的同时,没有传入对话框标题,则不渲染标题 * @default false */ disableCloseIcon?: boolean; /** * 是启用点击遮罩关闭 * @default false */ maskClosable?: boolean; } // 容器们 const ModalBackDrop = createRocket("ModalBackDrop", "div.@{prefix}-backdrop"); const ModalHeader = createRocket("ModalHeader", "div.@{prefix}-dialog__header"); const ModalBody = createRocket("ModalBody", "div.@{prefix}-dialog__body"); const ModalFooter = createRocket( "ModalFooter", "div.@{prefix}-dialog__footer", "div.@{prefix}-dialog__btnwrap" ); /** * 模态对话框组件 */ export function Modal({ visible, caption, size, onClose, onExited, disableEscape, disableCloseIcon, maskClosable, className, style, children, }: ModalProps) { const { classPrefix } = useConfig(); const ref = useRef(null); const { listen, ignoreProps } = useOutsideClick(ref); listen(() => maskClosable && visible && onClose()); // 监听 ESC 键 useEffect(() => { Eif (!visible) { return () => null; } const handleKeydown = (evt: KeyboardEvent) => { if (evt.keyCode === 27 && !disableEscape && onClose) { onClose(); } }; window.addEventListener("keydown", handleKeydown); return () => window.removeEventListener("keydown", handleKeydown); }, [visible, disableEscape, onClose]); const { shouldContentEnter, shouldContentRender, onContentExit, } = useVisibleTransition(visible); Eif (!shouldContentRender) { return null; } // 有标题,或者有图标,就需要渲染 header const hasHeader = Boolean(caption) || !disableCloseIcon; // 内置尺寸名称 let sizeClassName: string = null; if (typeof size === "string" && ["s", "l", "xl", "auto"].indexOf(size) > -1) { sizeClassName = `size-${size}`; } const dialog = ( <div className={`${classPrefix}-overlay ${classPrefix}-dialog-parent`}> <FadeTransition in={shouldContentEnter} opacity={0.5}> <ModalBackDrop /> </FadeTransition> <SlideTransition in={shouldContentEnter} onExited={callBoth(onExited, onContentExit)} > <div className={classNames(`${classPrefix}-dialog`, className)} style={style} > <div ref={ref} className={classNames( `${classPrefix}-dialog__inner`, sizeClassName )} style={size > 0 || /%$/.test(String(size)) ? { width: size } : null} {...ignoreProps} > {hasHeader && ( <ModalHeader> {caption && ( <h3 className={`${classPrefix}-dialog__headertitle`}> {caption} </h3> )} {!disableCloseIcon && <Button icon="close" onClick={onClose} />} </ModalHeader> )} {children} </div> </div> </SlideTransition> </div> ); return createPortal(dialog, getOverlayRoot()); } /** * 渲染对话框的主要内容 */ Modal.Body = ModalBody; /** * 渲染对话框的底部内容 */ Modal.Footer = ModalFooter; /** * 对话框消息内容,可置于 Modal.Body 中 */ Modal.Message = ModalMessage; |