This commit is contained in:
2026-05-25 17:08:18 +08:00
parent df501f6151
commit f1259b71b5
9178 changed files with 1626125 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright (c) 2014-present yiminghe
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+119
View File
@@ -0,0 +1,119 @@
# @rc-component/dialog
react dialog component.
[![NPM version][npm-image]][npm-url] [![npm download][download-image]][download-url] [![build status][github-actions-image]][github-actions-url] [![Codecov][codecov-image]][codecov-url] [![bundle size][bundlephobia-image]][bundlephobia-url] [![dumi][dumi-image]][dumi-url]
[npm-image]: https://img.shields.io/npm/v/@rc-component/dialog.svg?style=flat-square
[npm-url]: https://npmjs.org/package/@rc-component/dialog
[travis-image]: https://img.shields.io/travis/react-component/dialog/master?style=flat-square
[travis-url]: https://travis-ci.com/react-component/dialog
[github-actions-image]: https://github.com/react-component/dialog/actions/workflows/react-component-ci.yml/badge.svg
[github-actions-url]: https://github.com/react-component/dialog/actions/workflows/react-component-ci.yml
[codecov-image]: https://img.shields.io/codecov/c/github/react-component/dialog/master.svg?style=flat-square
[codecov-url]: https://app.codecov.io/gh/react-component/dialog
[david-url]: https://david-dm.org/react-component/dialog
[david-image]: https://david-dm.org/react-component/dialog/status.svg?style=flat-square
[david-dev-url]: https://david-dm.org/react-component/dialog?type=dev
[david-dev-image]: https://david-dm.org/react-component/dialog/dev-status.svg?style=flat-square
[download-image]: https://img.shields.io/npm/dm/@rc-component/dialog.svg?style=flat-square
[download-url]: https://npmjs.org/package/@rc-component/dialog
[bundlephobia-url]: https://bundlephobia.com/package/@rc-component/dialog
[bundlephobia-image]: https://badgen.net/bundlephobia/minzip/@rc-component/dialog
[dumi-url]: https://github.com/umijs/dumi
[dumi-image]: https://img.shields.io/badge/docs%20by-dumi-blue?style=flat-square
## Screenshot
<img src="http://gtms04.alicdn.com/tps/i4/TB1dp5lHXXXXXbmXpXXyVug.FXX-664-480.png" />
## Example
http://localhost:8007/examples/
online example: https://dialog.react-component.vercel.app/
## Install
[![@rc-component/dialog](https://nodei.co/npm/@rc-component/dialog.png)](https://npmjs.org/package/@rc-component/dialog)
## Usage
```js
var Dialog = require('@rc-component/dialog');
ReactDOM.render(
<Dialog title={title} onClose={callback1} visible>
<p>first dialog</p>
</Dialog>
), document.getElementById('t1'));
// use dialog
```
## API
### @rc-component/dialog
| Name | Type | Default | Description | Version |
| --- | --- | --- | --- | --- |
| prefixCls | String | rc-dialog | The dialog dom node's prefixCls | |
| className | String | | additional className for dialog | |
| classNames | { header?: string; body?: string; footer?: string; mask?: string; content?: string; wrapper?: string; } | | pass className to target area | |
| styles | { header?: CSSProperties; body?: CSSProperties; footer?: CSSProperties; mask?: CSSProperties; content?: CSSProperties; wrapper?: CSSProperties; } | | pass styles to target area | |
| style | Object | {} | Root style for dialog element.Such as width, height | |
| zIndex | Number | | | |
| visible | Boolean | false | current dialog's visible status | |
| animation | String | | part of dialog animation css class name | |
| maskAnimation | String | | part of dialog's mask animation css class name | |
| transitionName | String | | dialog animation css class name | |
| maskTransitionName | String | | mask animation css class name | |
| title | String\|React.Element | | Title of the dialog | |
| footer | React.Element | | footer of the dialog | |
| closable | Boolean \| ({ closeIcon?: React.ReactNode; disabled?: boolean, afterClose:function } & React.AriaAttributes) | true | whether show close button | |
| mask | Boolean | true | whether show mask | |
| maskClosable | Boolean | true | whether click mask to close | |
| keyboard | Boolean | true | whether support press esc to close | |
| mousePosition | {x:number,y:number} | | set pageX and pageY of current mouse(it will cause transform origin to be set). | |
| onClose | function() | | called when click close button or mask | |
| afterClose | function() | | called when close animation end | |
| getContainer | function(): HTMLElement | | to determine where Dialog will be mounted | |
| destroyOnHidden | Boolean | false | to unmount child compenents on onClose | |
| closeIcon | ReactNode | | specific the close icon. | |
| forceRender | Boolean | false | Create dialog dom node before dialog first show | |
| focusTriggerAfterClose | Boolean | true | focus trigger element when dialog closed | |
| modalRender | (node: ReactNode) => ReactNode | | Custom modal content render | 8.3.0 |
## Development
```
npm install
npm start
```
## Test Case
```
npm test
npm run chrome-test
```
## Coverage
```
npm run coverage
```
open coverage/ dir
## License
@rc-component/dialog is released under the MIT license.
## 🤝 Contributing
<a href="https://openomy.app/github/react-component/dialog" target="_blank" style="display: block; width: 100%;" align="center">
<img src="https://www.openomy.app/svg?repo=react-component/dialog&chart=bubble&latestMonth=24" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
</a>
+177
View File
@@ -0,0 +1,177 @@
.rc-dialog {
position: relative;
width: auto;
margin: 10px;
}
.rc-dialog-wrap {
position: fixed;
overflow: auto;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1050;
-webkit-overflow-scrolling: touch;
outline: 0;
}
.rc-dialog-container {
position: relative;
background-color: #fff;
border: 1px solid #999;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 6px;
box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
background-clip: padding-box;
outline: 0;
}
.rc-dialog-header {
padding: 15px;
border-bottom: 1px solid #e5e5e5;
}
.rc-dialog-close {
cursor: pointer;
border: 0;
background: transparent;
font-size: 21px;
position: absolute;
right: 20px;
top: 12px;
font-weight: 700;
line-height: 1;
color: #000;
text-shadow: 0 1px 0 #fff;
filter: alpha(opacity=20);
opacity: 0.2;
text-decoration: none;
}
.rc-dialog-close-x:after {
content: '×';
}
.rc-dialog-close:hover {
opacity: 1;
filter: alpha(opacity=100);
text-decoration: none;
}
.rc-dialog-title {
margin: 0;
line-height: 1.42857143;
}
.rc-dialog-body {
position: relative;
padding: 15px;
}
.rc-dialog-footer {
padding: 15px;
text-align: right;
border-top: 1px solid #e5e5e5;
}
.rc-dialog-footer .btn + .btn {
margin-left: 5px;
margin-bottom: 0;
}
.rc-dialog-footer .btn-group .btn + .btn {
margin-left: -1px;
}
.rc-dialog-footer .btn-block + .btn-block {
margin-left: 0;
}
@media (min-width: 768px) {
.rc-dialog {
width: 600px;
margin: 30px auto;
}
.rc-dialog-container {
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
}
}
.rc-dialog-slide-fade-enter,
.rc-dialog-slide-fade-appear {
transform: translate(0, -25%);
}
.rc-dialog-slide-fade-enter,
.rc-dialog-slide-fade-appear,
.rc-dialog-slide-fade-leave {
animation-duration: 0.3s;
animation-fill-mode: both;
animation-timing-function: ease-out;
animation-play-state: paused;
}
.rc-dialog-slide-fade-enter.rc-dialog-slide-fade-enter-active,
.rc-dialog-slide-fade-appear.rc-dialog-slide-fade-appear-active {
animation-name: rcDialogSlideFadeIn;
animation-play-state: running;
}
.rc-dialog-slide-fade-leave.rc-dialog-slide-fade-leave-active {
animation-name: rcDialogSlideFadeOut;
animation-play-state: running;
}
@keyframes rcDialogSlideFadeIn {
0% {
transform: translate(0, -25%);
}
100% {
transform: translate(0, 0);
}
}
@keyframes rcDialogSlideFadeOut {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(0, -25%);
}
}
.rc-dialog-mask {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
background-color: #373737;
background-color: rgba(55, 55, 55, 0.6);
height: 100%;
filter: alpha(opacity=50);
z-index: 1050;
}
.rc-dialog-mask-hidden {
display: none;
}
.rc-dialog-fade-enter,
.rc-dialog-fade-appear {
opacity: 0;
animation-duration: 0.3s;
animation-fill-mode: both;
animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2);
animation-play-state: paused;
}
.rc-dialog-fade-leave {
animation-duration: 0.3s;
animation-fill-mode: both;
animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2);
animation-play-state: paused;
}
.rc-dialog-fade-enter.rc-dialog-fade-enter-active,
.rc-dialog-fade-appear.rc-dialog-fade-appear-active {
animation-name: rcDialogFadeIn;
animation-play-state: running;
}
.rc-dialog-fade-leave.rc-dialog-fade-leave-active {
animation-name: rcDialogFadeOut;
animation-play-state: running;
}
@keyframes rcDialogFadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes rcDialogFadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
+174
View File
@@ -0,0 +1,174 @@
.rc-dialog {
position: relative;
width: auto;
margin: 10px;
}
.rc-dialog-wrap {
position: fixed;
overflow: auto;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1050;
-webkit-overflow-scrolling: touch;
outline: 0;
}
.rc-dialog-title {
margin: 0;
font-size: 14px;
line-height: 21px;
font-weight: bold;
}
.rc-dialog-container {
position: relative;
background-color: #ffffff;
border: none;
border-radius: 6px 6px;
background-clip: padding-box;
}
.rc-dialog-close {
cursor: pointer;
border: 0;
background: transparent;
font-size: 21px;
position: absolute;
right: 20px;
top: 12px;
font-weight: 700;
line-height: 1;
color: #000;
text-shadow: 0 1px 0 #fff;
filter: alpha(opacity=20);
opacity: 0.2;
text-decoration: none;
}
.rc-dialog-close:disabled {
pointer-events: none;
}
.rc-dialog-close-x:after {
content: '×';
}
.rc-dialog-close:hover {
opacity: 1;
filter: alpha(opacity=100);
text-decoration: none;
}
.rc-dialog-header {
padding: 13px 20px 14px 20px;
border-radius: 5px 5px 0 0;
background: #fff;
color: #666;
border-bottom: 1px solid #e9e9e9;
}
.rc-dialog-body {
padding: 20px;
}
.rc-dialog-footer {
border-top: 1px solid #e9e9e9;
padding: 10px 20px;
text-align: right;
border-radius: 0 0 5px 5px;
}
.rc-dialog-zoom-enter,
.rc-dialog-zoom-appear {
opacity: 0;
animation-duration: 0.3s;
animation-fill-mode: both;
animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
animation-play-state: paused;
}
.rc-dialog-zoom-leave {
animation-duration: 0.3s;
animation-fill-mode: both;
animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34);
animation-play-state: paused;
}
.rc-dialog-zoom-enter.rc-dialog-zoom-enter-active,
.rc-dialog-zoom-appear.rc-dialog-zoom-appear-active {
animation-name: rcDialogZoomIn;
animation-play-state: running;
}
.rc-dialog-zoom-leave.rc-dialog-zoom-leave-active {
animation-name: rcDialogZoomOut;
animation-play-state: running;
}
@keyframes rcDialogZoomIn {
0% {
opacity: 0;
transform: scale(0, 0);
}
100% {
opacity: 1;
transform: scale(1, 1);
}
}
@keyframes rcDialogZoomOut {
0% {
transform: scale(1, 1);
}
100% {
opacity: 0;
transform: scale(0, 0);
}
}
@media (min-width: 768px) {
.rc-dialog {
width: 600px;
margin: 30px auto;
}
}
.rc-dialog-mask {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
background-color: #373737;
background-color: rgba(55, 55, 55, 0.6);
height: 100%;
filter: alpha(opacity=50);
z-index: 1050;
}
.rc-dialog-mask-hidden {
display: none;
}
.rc-dialog-fade-enter,
.rc-dialog-fade-appear {
opacity: 0;
animation-duration: 0.3s;
animation-fill-mode: both;
animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2);
animation-play-state: paused;
}
.rc-dialog-fade-leave {
animation-duration: 0.3s;
animation-fill-mode: both;
animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2);
animation-play-state: paused;
}
.rc-dialog-fade-enter.rc-dialog-fade-enter-active,
.rc-dialog-fade-appear.rc-dialog-fade-appear-active {
animation-name: rcDialogFadeIn;
animation-play-state: running;
}
.rc-dialog-fade-leave.rc-dialog-fade-leave-active {
animation-name: rcDialogFadeOut;
animation-play-state: running;
}
@keyframes rcDialogFadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes rcDialogFadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@@ -0,0 +1,7 @@
import * as React from 'react';
export type MemoChildrenProps = {
shouldUpdate: boolean;
children: React.ReactNode;
};
declare const _default: React.MemoExoticComponent<({ children }: MemoChildrenProps) => React.ReactElement<any, string | React.JSXElementConstructor<any>>>;
export default _default;
@@ -0,0 +1,6 @@
import * as React from 'react';
export default /*#__PURE__*/React.memo(({
children
}) => children, (_, {
shouldUpdate
}) => !shouldUpdate);
@@ -0,0 +1,16 @@
import React from 'react';
import type { IDialogPropTypes } from '../../IDialogPropTypes';
export interface PanelProps extends Omit<IDialogPropTypes, 'getOpenCount'> {
prefixCls: string;
ariaId?: string;
onMouseDown?: React.MouseEventHandler;
onMouseUp?: React.MouseEventHandler;
holderRef?: React.Ref<HTMLDivElement>;
/** Used for focus lock. When true and open, focus will lock into the panel */
isFixedPos?: boolean;
}
export type PanelRef = {
focus: () => void;
};
declare const Panel: React.ForwardRefExoticComponent<PanelProps & React.RefAttributes<PanelRef>>;
export default Panel;
@@ -0,0 +1,136 @@
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import { clsx } from 'clsx';
import { useComposeRef } from "@rc-component/util/es/ref";
import { useLockFocus } from "@rc-component/util/es/Dom/focus";
import React, { useMemo, useRef } from 'react';
import { RefContext } from "../../context";
import MemoChildren from "./MemoChildren";
import pickAttrs from "@rc-component/util/es/pickAttrs";
const Panel = /*#__PURE__*/React.forwardRef((props, ref) => {
const {
prefixCls,
className,
style,
title,
ariaId,
footer,
closable,
closeIcon,
onClose,
children,
bodyStyle,
bodyProps,
modalRender,
onMouseDown,
onMouseUp,
holderRef,
visible,
forceRender,
width,
height,
classNames: modalClassNames,
styles: modalStyles,
isFixedPos,
focusTrap
} = props;
// ================================= Refs =================================
const {
panel: panelRef
} = React.useContext(RefContext);
const internalRef = useRef(null);
const mergedRef = useComposeRef(holderRef, panelRef, internalRef);
const [ignoreElement] = useLockFocus(visible && isFixedPos && focusTrap !== false, () => internalRef.current);
React.useImperativeHandle(ref, () => ({
focus: () => {
internalRef.current?.focus({
preventScroll: true
});
}
}));
// ================================ Style =================================
const contentStyle = {};
if (width !== undefined) {
contentStyle.width = width;
}
if (height !== undefined) {
contentStyle.height = height;
}
// ================================ Render ================================
const footerNode = footer ? /*#__PURE__*/React.createElement("div", {
className: clsx(`${prefixCls}-footer`, modalClassNames?.footer),
style: {
...modalStyles?.footer
}
}, footer) : null;
const headerNode = title ? /*#__PURE__*/React.createElement("div", {
className: clsx(`${prefixCls}-header`, modalClassNames?.header),
style: {
...modalStyles?.header
}
}, /*#__PURE__*/React.createElement("div", {
className: clsx(`${prefixCls}-title`, modalClassNames?.title),
id: ariaId,
style: {
...modalStyles?.title
}
}, title)) : null;
const closableObj = useMemo(() => {
if (typeof closable === 'object' && closable !== null) {
return closable;
}
if (closable) {
return {
closeIcon: closeIcon ?? /*#__PURE__*/React.createElement("span", {
className: `${prefixCls}-close-x`
})
};
}
return {};
}, [closable, closeIcon, prefixCls]);
const ariaProps = pickAttrs(closableObj, true);
const closeBtnIsDisabled = typeof closable === 'object' && closable.disabled;
const closerNode = closable ? /*#__PURE__*/React.createElement("button", _extends({
type: "button",
onClick: onClose,
"aria-label": "Close"
}, ariaProps, {
className: `${prefixCls}-close`,
disabled: closeBtnIsDisabled
}), closableObj.closeIcon) : null;
const content = /*#__PURE__*/React.createElement("div", {
className: clsx(`${prefixCls}-container`, modalClassNames?.container),
style: modalStyles?.container
}, closerNode, headerNode, /*#__PURE__*/React.createElement("div", _extends({
className: clsx(`${prefixCls}-body`, modalClassNames?.body),
style: {
...bodyStyle,
...modalStyles?.body
}
}, bodyProps), children), footerNode);
return /*#__PURE__*/React.createElement("div", {
key: "dialog-element",
role: "dialog",
"aria-labelledby": title ? ariaId : null,
"aria-modal": "true",
ref: mergedRef,
style: {
...style,
...contentStyle
},
className: clsx(prefixCls, className),
onMouseDown: onMouseDown,
onMouseUp: onMouseUp,
tabIndex: -1,
onFocus: e => {
ignoreElement(e.target);
}
}, /*#__PURE__*/React.createElement(MemoChildren, {
shouldUpdate: visible || forceRender
}, modalRender ? modalRender(content) : content));
});
if (process.env.NODE_ENV !== 'production') {
Panel.displayName = 'Panel';
}
export default Panel;
@@ -0,0 +1,16 @@
import * as React from 'react';
import type { PanelProps, PanelRef } from './Panel';
import type { CSSMotionRef } from '@rc-component/motion/es/CSSMotion';
export type CSSMotionStateRef = Pick<CSSMotionRef, 'inMotion' | 'enableMotion'>;
export type ContentRef = PanelRef & CSSMotionStateRef;
export type ContentProps = {
motionName: string;
ariaId: string;
onVisibleChanged: (visible: boolean) => void;
} & PanelProps;
declare const Content: React.ForwardRefExoticComponent<{
motionName: string;
ariaId: string;
onVisibleChanged: (visible: boolean) => void;
} & PanelProps & React.RefAttributes<ContentRef>>;
export default Content;
@@ -0,0 +1,76 @@
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import * as React from 'react';
import { useRef } from 'react';
import { clsx } from 'clsx';
import CSSMotion from '@rc-component/motion';
import { offset } from "../../util";
import Panel from "./Panel";
const Content = /*#__PURE__*/React.forwardRef((props, ref) => {
const {
prefixCls,
title,
style,
className,
visible,
forceRender,
destroyOnHidden,
motionName,
ariaId,
onVisibleChanged,
mousePosition
} = props;
const dialogRef = useRef(null);
const panelRef = useRef(null);
// ============================== Refs ==============================
React.useImperativeHandle(ref, () => ({
...panelRef.current,
inMotion: dialogRef.current.inMotion,
enableMotion: dialogRef.current.enableMotion
}));
// ============================= Style ==============================
const [transformOrigin, setTransformOrigin] = React.useState();
const contentStyle = {};
if (transformOrigin) {
contentStyle.transformOrigin = transformOrigin;
}
function onPrepare() {
if (!dialogRef.current?.nativeElement) {
return;
}
const elementOffset = offset(dialogRef.current.nativeElement);
setTransformOrigin(mousePosition && (mousePosition.x || mousePosition.y) ? `${mousePosition.x - elementOffset.left}px ${mousePosition.y - elementOffset.top}px` : '');
}
// ============================= Render =============================
return /*#__PURE__*/React.createElement(CSSMotion, {
visible: visible,
onVisibleChanged: onVisibleChanged,
onAppearPrepare: onPrepare,
onEnterPrepare: onPrepare,
forceRender: forceRender,
motionName: motionName,
removeOnLeave: destroyOnHidden,
ref: dialogRef
}, ({
className: motionClassName,
style: motionStyle
}, motionRef) => /*#__PURE__*/React.createElement(Panel, _extends({}, props, {
ref: panelRef,
title: title,
ariaId: ariaId,
prefixCls: prefixCls,
holderRef: motionRef,
style: {
...motionStyle,
...style,
...contentStyle
},
className: clsx(className, motionClassName)
})));
});
if (process.env.NODE_ENV !== 'production') {
Content.displayName = 'Content';
}
export default Content;
+11
View File
@@ -0,0 +1,11 @@
import * as React from 'react';
export type MaskProps = {
prefixCls: string;
visible: boolean;
motionName?: string;
style?: React.CSSProperties;
maskProps?: React.HTMLAttributes<HTMLDivElement>;
className?: string;
};
declare const Mask: React.FC<MaskProps>;
export default Mask;
+31
View File
@@ -0,0 +1,31 @@
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import * as React from 'react';
import { clsx } from 'clsx';
import CSSMotion from '@rc-component/motion';
const Mask = props => {
const {
prefixCls,
style,
visible,
maskProps,
motionName,
className
} = props;
return /*#__PURE__*/React.createElement(CSSMotion, {
key: "mask",
visible: visible,
motionName: motionName,
leavedClassName: `${prefixCls}-mask-hidden`
}, ({
className: motionClassName,
style: motionStyle
}, ref) => /*#__PURE__*/React.createElement("div", _extends({
ref: ref,
style: {
...motionStyle,
...style
},
className: clsx(`${prefixCls}-mask`, motionClassName, className)
}, maskProps)));
};
export default Mask;
@@ -0,0 +1,4 @@
import * as React from 'react';
import type { IDialogPropTypes } from '../IDialogPropTypes';
declare const Dialog: React.FC<IDialogPropTypes>;
export default Dialog;
+178
View File
@@ -0,0 +1,178 @@
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import { clsx } from 'clsx';
import contains from "@rc-component/util/es/Dom/contains";
import useId from "@rc-component/util/es/hooks/useId";
import pickAttrs from "@rc-component/util/es/pickAttrs";
import * as React from 'react';
import { useEffect, useRef } from 'react';
import { getMotionName } from "../util";
import Content from "./Content";
import Mask from "./Mask";
import { warning } from "@rc-component/util/es/warning";
const Dialog = props => {
const {
prefixCls = 'rc-dialog',
zIndex,
visible = false,
focusTriggerAfterClose = true,
wrapStyle,
wrapClassName,
wrapProps,
onClose,
afterOpenChange,
afterClose,
// Dialog
transitionName,
animation,
closable = true,
// Mask
mask = true,
maskTransitionName,
maskAnimation,
maskClosable = true,
maskStyle,
maskProps,
rootClassName,
rootStyle,
classNames: modalClassNames,
styles: modalStyles
} = props;
if (process.env.NODE_ENV !== 'production') {
['wrapStyle', 'bodyStyle', 'maskStyle'].forEach(prop => {
warning(!(prop in props), `${prop} is deprecated, please use styles instead.`);
});
if ('wrapClassName' in props) {
warning(false, `wrapClassName is deprecated, please use classNames instead.`);
}
}
const lastOutSideActiveElementRef = useRef(null);
const wrapperRef = useRef(null);
const contentRef = useRef(null);
const [animatedVisible, setAnimatedVisible] = React.useState(visible);
const [isFixedPos, setIsFixedPos] = React.useState(false);
// ========================== Init ==========================
const ariaId = useId();
function saveLastOutSideActiveElementRef() {
if (!contains(wrapperRef.current, document.activeElement)) {
lastOutSideActiveElementRef.current = document.activeElement;
}
}
function focusDialogContent() {
if (!contains(wrapperRef.current, document.activeElement)) {
contentRef.current?.focus();
}
}
// ========================= Events =========================
// Close action will trigger by:
// 1. When hide motion end
// 2. Controlled `open` to `false` immediately after set to `true` which will not trigger motion
function doClose() {
// Clean up scroll bar & focus back
setAnimatedVisible(false);
if (mask && lastOutSideActiveElementRef.current && focusTriggerAfterClose) {
try {
lastOutSideActiveElementRef.current.focus({
preventScroll: true
});
} catch (e) {
// Do nothing
}
lastOutSideActiveElementRef.current = null;
}
// Trigger afterClose only when change visible from true to false
if (animatedVisible) {
afterClose?.();
}
}
function onDialogVisibleChanged(newVisible) {
// Try to focus
if (newVisible) {
focusDialogContent();
} else {
doClose();
}
afterOpenChange?.(newVisible);
}
function onInternalClose(e) {
onClose?.(e);
}
// >>> Content
const mouseDownOnMaskRef = useRef(false);
// >>> Wrapper
// Close only when element not on dialog
let onWrapperClick = null;
if (maskClosable) {
onWrapperClick = e => {
if (wrapperRef.current === e.target && mouseDownOnMaskRef.current) {
onInternalClose(e);
}
};
}
function onWrapperMouseDown(e) {
mouseDownOnMaskRef.current = e.target === wrapperRef.current;
}
// ========================= Effect =========================
useEffect(() => {
if (visible) {
mouseDownOnMaskRef.current = false;
setAnimatedVisible(true);
saveLastOutSideActiveElementRef();
// Calc the position style
if (wrapperRef.current) {
const computedWrapStyle = getComputedStyle(wrapperRef.current);
setIsFixedPos(computedWrapStyle.position === 'fixed');
}
} else if (animatedVisible && contentRef.current.enableMotion() && !contentRef.current.inMotion()) {
doClose();
}
}, [visible]);
const mergedStyle = {
zIndex,
...wrapStyle,
...modalStyles?.wrapper,
display: !animatedVisible ? 'none' : null
};
// ========================= Render =========================
return /*#__PURE__*/React.createElement("div", _extends({
className: clsx(`${prefixCls}-root`, rootClassName),
style: rootStyle
}, pickAttrs(props, {
data: true
})), /*#__PURE__*/React.createElement(Mask, {
prefixCls: prefixCls,
visible: mask && visible,
motionName: getMotionName(prefixCls, maskTransitionName, maskAnimation),
style: {
zIndex,
...maskStyle,
...modalStyles?.mask
},
maskProps: maskProps,
className: modalClassNames?.mask
}), /*#__PURE__*/React.createElement("div", _extends({
className: clsx(`${prefixCls}-wrap`, wrapClassName, modalClassNames?.wrapper),
ref: wrapperRef,
onClick: onWrapperClick,
onMouseDown: onWrapperMouseDown,
style: mergedStyle
}, wrapProps), /*#__PURE__*/React.createElement(Content, _extends({}, props, {
isFixedPos: isFixedPos,
ref: contentRef,
closable: closable,
ariaId: ariaId,
prefixCls: prefixCls,
visible: visible && animatedVisible,
onClose: onInternalClose,
onVisibleChanged: onDialogVisibleChanged,
motionName: getMotionName(prefixCls, transitionName, animation)
}))));
};
export default Dialog;
+4
View File
@@ -0,0 +1,4 @@
import * as React from 'react';
import type { IDialogPropTypes } from './IDialogPropTypes';
declare const DialogWrap: React.FC<IDialogPropTypes>;
export default DialogWrap;
+75
View File
@@ -0,0 +1,75 @@
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import Portal from '@rc-component/portal';
import * as React from 'react';
import { RefContext } from "./context";
import Dialog from "./Dialog";
// fix issue #10656
/*
* getContainer remarks
* Custom container should not be return, because in the Portal component, it will remove the
* return container element here, if the custom container is the only child of it's component,
* like issue #10656, It will has a conflict with removeChild method in react-dom.
* So here should add a child (div element) to custom container.
* */
const DialogWrap = props => {
const {
visible,
getContainer,
forceRender,
destroyOnHidden = false,
afterClose,
closable,
panelRef,
keyboard = true,
onClose
} = props;
const [animatedVisible, setAnimatedVisible] = React.useState(visible);
const refContext = React.useMemo(() => ({
panel: panelRef
}), [panelRef]);
const onEsc = ({
top,
event
}) => {
if (top && keyboard) {
event.stopPropagation();
onClose?.(event);
return;
}
};
React.useEffect(() => {
if (visible) {
setAnimatedVisible(true);
}
}, [visible]);
// Destroy on close will remove wrapped div
if (!forceRender && destroyOnHidden && !animatedVisible) {
return null;
}
return /*#__PURE__*/React.createElement(RefContext.Provider, {
value: refContext
}, /*#__PURE__*/React.createElement(Portal, {
open: visible || forceRender || animatedVisible,
onEsc: onEsc,
autoDestroy: false,
getContainer: getContainer,
autoLock: visible || animatedVisible
}, /*#__PURE__*/React.createElement(Dialog, _extends({}, props, {
destroyOnHidden: destroyOnHidden,
afterClose: () => {
const closableObj = closable && typeof closable === 'object' ? closable : {};
const {
afterClose: closableAfterClose
} = closableObj || {};
closableAfterClose?.();
afterClose?.();
setAnimatedVisible(false);
}
}))));
};
if (process.env.NODE_ENV !== 'production') {
DialogWrap.displayName = 'Dialog';
}
export default DialogWrap;
@@ -0,0 +1,56 @@
import type { GetContainer } from '@rc-component/util/lib/PortalWrapper';
import type { CSSProperties, ReactNode, SyntheticEvent } from 'react';
export type SemanticName = 'header' | 'body' | 'footer' | 'container' | 'title' | 'wrapper' | 'mask';
export type ModalClassNames = Partial<Record<SemanticName, string>>;
export type ModalStyles = Partial<Record<SemanticName, CSSProperties>>;
export type ClosableType = {
closeIcon?: React.ReactNode;
disabled?: boolean;
afterClose?: () => any;
};
export type IDialogPropTypes = {
className?: string;
keyboard?: boolean;
style?: CSSProperties;
rootStyle?: CSSProperties;
mask?: boolean;
children?: React.ReactNode;
afterClose?: () => any;
afterOpenChange?: (open: boolean) => void;
onClose?: (e: SyntheticEvent | KeyboardEvent) => any;
closable?: boolean | (ClosableType & React.AriaAttributes);
maskClosable?: boolean;
visible?: boolean;
destroyOnHidden?: boolean;
mousePosition?: {
x: number;
y: number;
} | null;
title?: ReactNode;
footer?: ReactNode;
transitionName?: string;
maskTransitionName?: string;
animation?: any;
maskAnimation?: any;
wrapStyle?: Record<string, any>;
bodyStyle?: Record<string, any>;
maskStyle?: Record<string, any>;
prefixCls?: string;
wrapClassName?: string;
width?: string | number;
height?: string | number;
zIndex?: number;
bodyProps?: any;
maskProps?: any;
rootClassName?: string;
classNames?: ModalClassNames;
styles?: ModalStyles;
wrapProps?: any;
getContainer?: GetContainer | false;
closeIcon?: ReactNode;
modalRender?: (node: ReactNode) => ReactNode;
forceRender?: boolean;
focusTriggerAfterClose?: boolean;
focusTrap?: boolean;
panelRef?: React.Ref<HTMLDivElement>;
};
@@ -0,0 +1 @@
export {};
+5
View File
@@ -0,0 +1,5 @@
import * as React from 'react';
export interface RefContextProps {
panel?: React.Ref<HTMLDivElement>;
}
export declare const RefContext: React.Context<RefContextProps>;
+2
View File
@@ -0,0 +1,2 @@
import * as React from 'react';
export const RefContext = /*#__PURE__*/React.createContext({});
+6
View File
@@ -0,0 +1,6 @@
import DialogWrap from './DialogWrap';
import Panel from './Dialog/Content/Panel';
import type { IDialogPropTypes as DialogProps } from './IDialogPropTypes';
export type { DialogProps };
export { Panel };
export default DialogWrap;
+4
View File
@@ -0,0 +1,4 @@
import DialogWrap from "./DialogWrap";
import Panel from "./Dialog/Content/Panel";
export { Panel };
export default DialogWrap;
+5
View File
@@ -0,0 +1,5 @@
export declare function getMotionName(prefixCls: string, transitionName?: string, animationName?: string): string;
export declare function offset(el: Element): {
left: number;
top: number;
};
+34
View File
@@ -0,0 +1,34 @@
// =============================== Motion ===============================
export function getMotionName(prefixCls, transitionName, animationName) {
let motionName = transitionName;
if (!motionName && animationName) {
motionName = `${prefixCls}-${animationName}`;
}
return motionName;
}
// =============================== Offset ===============================
function getScroll(w, top) {
let ret = w[`page${top ? 'Y' : 'X'}Offset`];
const method = `scroll${top ? 'Top' : 'Left'}`;
if (typeof ret !== 'number') {
const d = w.document;
ret = d.documentElement[method];
if (typeof ret !== 'number') {
ret = d.body[method];
}
}
return ret;
}
export function offset(el) {
const rect = el.getBoundingClientRect();
const pos = {
left: rect.left,
top: rect.top
};
const doc = el.ownerDocument;
const w = doc.defaultView || doc.parentWindow;
pos.left += getScroll(w);
pos.top += getScroll(w, true);
return pos;
}
@@ -0,0 +1,7 @@
import * as React from 'react';
export type MemoChildrenProps = {
shouldUpdate: boolean;
children: React.ReactNode;
};
declare const _default: React.MemoExoticComponent<({ children }: MemoChildrenProps) => React.ReactElement<any, string | React.JSXElementConstructor<any>>>;
export default _default;
@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var React = _interopRequireWildcard(require("react"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
var _default = exports.default = /*#__PURE__*/React.memo(({
children
}) => children, (_, {
shouldUpdate
}) => !shouldUpdate);
@@ -0,0 +1,16 @@
import React from 'react';
import type { IDialogPropTypes } from '../../IDialogPropTypes';
export interface PanelProps extends Omit<IDialogPropTypes, 'getOpenCount'> {
prefixCls: string;
ariaId?: string;
onMouseDown?: React.MouseEventHandler;
onMouseUp?: React.MouseEventHandler;
holderRef?: React.Ref<HTMLDivElement>;
/** Used for focus lock. When true and open, focus will lock into the panel */
isFixedPos?: boolean;
}
export type PanelRef = {
focus: () => void;
};
declare const Panel: React.ForwardRefExoticComponent<PanelProps & React.RefAttributes<PanelRef>>;
export default Panel;
@@ -0,0 +1,145 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _clsx = require("clsx");
var _ref = require("@rc-component/util/lib/ref");
var _focus = require("@rc-component/util/lib/Dom/focus");
var _react = _interopRequireWildcard(require("react"));
var _context = require("../../context");
var _MemoChildren = _interopRequireDefault(require("./MemoChildren"));
var _pickAttrs = _interopRequireDefault(require("@rc-component/util/lib/pickAttrs"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
const Panel = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
const {
prefixCls,
className,
style,
title,
ariaId,
footer,
closable,
closeIcon,
onClose,
children,
bodyStyle,
bodyProps,
modalRender,
onMouseDown,
onMouseUp,
holderRef,
visible,
forceRender,
width,
height,
classNames: modalClassNames,
styles: modalStyles,
isFixedPos,
focusTrap
} = props;
// ================================= Refs =================================
const {
panel: panelRef
} = _react.default.useContext(_context.RefContext);
const internalRef = (0, _react.useRef)(null);
const mergedRef = (0, _ref.useComposeRef)(holderRef, panelRef, internalRef);
const [ignoreElement] = (0, _focus.useLockFocus)(visible && isFixedPos && focusTrap !== false, () => internalRef.current);
_react.default.useImperativeHandle(ref, () => ({
focus: () => {
internalRef.current?.focus({
preventScroll: true
});
}
}));
// ================================ Style =================================
const contentStyle = {};
if (width !== undefined) {
contentStyle.width = width;
}
if (height !== undefined) {
contentStyle.height = height;
}
// ================================ Render ================================
const footerNode = footer ? /*#__PURE__*/_react.default.createElement("div", {
className: (0, _clsx.clsx)(`${prefixCls}-footer`, modalClassNames?.footer),
style: {
...modalStyles?.footer
}
}, footer) : null;
const headerNode = title ? /*#__PURE__*/_react.default.createElement("div", {
className: (0, _clsx.clsx)(`${prefixCls}-header`, modalClassNames?.header),
style: {
...modalStyles?.header
}
}, /*#__PURE__*/_react.default.createElement("div", {
className: (0, _clsx.clsx)(`${prefixCls}-title`, modalClassNames?.title),
id: ariaId,
style: {
...modalStyles?.title
}
}, title)) : null;
const closableObj = (0, _react.useMemo)(() => {
if (typeof closable === 'object' && closable !== null) {
return closable;
}
if (closable) {
return {
closeIcon: closeIcon ?? /*#__PURE__*/_react.default.createElement("span", {
className: `${prefixCls}-close-x`
})
};
}
return {};
}, [closable, closeIcon, prefixCls]);
const ariaProps = (0, _pickAttrs.default)(closableObj, true);
const closeBtnIsDisabled = typeof closable === 'object' && closable.disabled;
const closerNode = closable ? /*#__PURE__*/_react.default.createElement("button", _extends({
type: "button",
onClick: onClose,
"aria-label": "Close"
}, ariaProps, {
className: `${prefixCls}-close`,
disabled: closeBtnIsDisabled
}), closableObj.closeIcon) : null;
const content = /*#__PURE__*/_react.default.createElement("div", {
className: (0, _clsx.clsx)(`${prefixCls}-container`, modalClassNames?.container),
style: modalStyles?.container
}, closerNode, headerNode, /*#__PURE__*/_react.default.createElement("div", _extends({
className: (0, _clsx.clsx)(`${prefixCls}-body`, modalClassNames?.body),
style: {
...bodyStyle,
...modalStyles?.body
}
}, bodyProps), children), footerNode);
return /*#__PURE__*/_react.default.createElement("div", {
key: "dialog-element",
role: "dialog",
"aria-labelledby": title ? ariaId : null,
"aria-modal": "true",
ref: mergedRef,
style: {
...style,
...contentStyle
},
className: (0, _clsx.clsx)(prefixCls, className),
onMouseDown: onMouseDown,
onMouseUp: onMouseUp,
tabIndex: -1,
onFocus: e => {
ignoreElement(e.target);
}
}, /*#__PURE__*/_react.default.createElement(_MemoChildren.default, {
shouldUpdate: visible || forceRender
}, modalRender ? modalRender(content) : content));
});
if (process.env.NODE_ENV !== 'production') {
Panel.displayName = 'Panel';
}
var _default = exports.default = Panel;
@@ -0,0 +1,16 @@
import * as React from 'react';
import type { PanelProps, PanelRef } from './Panel';
import type { CSSMotionRef } from '@rc-component/motion/es/CSSMotion';
export type CSSMotionStateRef = Pick<CSSMotionRef, 'inMotion' | 'enableMotion'>;
export type ContentRef = PanelRef & CSSMotionStateRef;
export type ContentProps = {
motionName: string;
ariaId: string;
onVisibleChanged: (visible: boolean) => void;
} & PanelProps;
declare const Content: React.ForwardRefExoticComponent<{
motionName: string;
ariaId: string;
onVisibleChanged: (visible: boolean) => void;
} & PanelProps & React.RefAttributes<ContentRef>>;
export default Content;
@@ -0,0 +1,85 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var React = _react;
var _clsx = require("clsx");
var _motion = _interopRequireDefault(require("@rc-component/motion"));
var _util = require("../../util");
var _Panel = _interopRequireDefault(require("./Panel"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
const Content = /*#__PURE__*/React.forwardRef((props, ref) => {
const {
prefixCls,
title,
style,
className,
visible,
forceRender,
destroyOnHidden,
motionName,
ariaId,
onVisibleChanged,
mousePosition
} = props;
const dialogRef = (0, _react.useRef)(null);
const panelRef = (0, _react.useRef)(null);
// ============================== Refs ==============================
React.useImperativeHandle(ref, () => ({
...panelRef.current,
inMotion: dialogRef.current.inMotion,
enableMotion: dialogRef.current.enableMotion
}));
// ============================= Style ==============================
const [transformOrigin, setTransformOrigin] = React.useState();
const contentStyle = {};
if (transformOrigin) {
contentStyle.transformOrigin = transformOrigin;
}
function onPrepare() {
if (!dialogRef.current?.nativeElement) {
return;
}
const elementOffset = (0, _util.offset)(dialogRef.current.nativeElement);
setTransformOrigin(mousePosition && (mousePosition.x || mousePosition.y) ? `${mousePosition.x - elementOffset.left}px ${mousePosition.y - elementOffset.top}px` : '');
}
// ============================= Render =============================
return /*#__PURE__*/React.createElement(_motion.default, {
visible: visible,
onVisibleChanged: onVisibleChanged,
onAppearPrepare: onPrepare,
onEnterPrepare: onPrepare,
forceRender: forceRender,
motionName: motionName,
removeOnLeave: destroyOnHidden,
ref: dialogRef
}, ({
className: motionClassName,
style: motionStyle
}, motionRef) => /*#__PURE__*/React.createElement(_Panel.default, _extends({}, props, {
ref: panelRef,
title: title,
ariaId: ariaId,
prefixCls: prefixCls,
holderRef: motionRef,
style: {
...motionStyle,
...style,
...contentStyle
},
className: (0, _clsx.clsx)(className, motionClassName)
})));
});
if (process.env.NODE_ENV !== 'production') {
Content.displayName = 'Content';
}
var _default = exports.default = Content;
+11
View File
@@ -0,0 +1,11 @@
import * as React from 'react';
export type MaskProps = {
prefixCls: string;
visible: boolean;
motionName?: string;
style?: React.CSSProperties;
maskProps?: React.HTMLAttributes<HTMLDivElement>;
className?: string;
};
declare const Mask: React.FC<MaskProps>;
export default Mask;
+40
View File
@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var React = _interopRequireWildcard(require("react"));
var _clsx = require("clsx");
var _motion = _interopRequireDefault(require("@rc-component/motion"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
const Mask = props => {
const {
prefixCls,
style,
visible,
maskProps,
motionName,
className
} = props;
return /*#__PURE__*/React.createElement(_motion.default, {
key: "mask",
visible: visible,
motionName: motionName,
leavedClassName: `${prefixCls}-mask-hidden`
}, ({
className: motionClassName,
style: motionStyle
}, ref) => /*#__PURE__*/React.createElement("div", _extends({
ref: ref,
style: {
...motionStyle,
...style
},
className: (0, _clsx.clsx)(`${prefixCls}-mask`, motionClassName, className)
}, maskProps)));
};
var _default = exports.default = Mask;
@@ -0,0 +1,4 @@
import * as React from 'react';
import type { IDialogPropTypes } from '../IDialogPropTypes';
declare const Dialog: React.FC<IDialogPropTypes>;
export default Dialog;
+187
View File
@@ -0,0 +1,187 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _clsx = require("clsx");
var _contains = _interopRequireDefault(require("@rc-component/util/lib/Dom/contains"));
var _useId = _interopRequireDefault(require("@rc-component/util/lib/hooks/useId"));
var _pickAttrs = _interopRequireDefault(require("@rc-component/util/lib/pickAttrs"));
var _react = _interopRequireWildcard(require("react"));
var React = _react;
var _util = require("../util");
var _Content = _interopRequireDefault(require("./Content"));
var _Mask = _interopRequireDefault(require("./Mask"));
var _warning = require("@rc-component/util/lib/warning");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
const Dialog = props => {
const {
prefixCls = 'rc-dialog',
zIndex,
visible = false,
focusTriggerAfterClose = true,
wrapStyle,
wrapClassName,
wrapProps,
onClose,
afterOpenChange,
afterClose,
// Dialog
transitionName,
animation,
closable = true,
// Mask
mask = true,
maskTransitionName,
maskAnimation,
maskClosable = true,
maskStyle,
maskProps,
rootClassName,
rootStyle,
classNames: modalClassNames,
styles: modalStyles
} = props;
if (process.env.NODE_ENV !== 'production') {
['wrapStyle', 'bodyStyle', 'maskStyle'].forEach(prop => {
(0, _warning.warning)(!(prop in props), `${prop} is deprecated, please use styles instead.`);
});
if ('wrapClassName' in props) {
(0, _warning.warning)(false, `wrapClassName is deprecated, please use classNames instead.`);
}
}
const lastOutSideActiveElementRef = (0, _react.useRef)(null);
const wrapperRef = (0, _react.useRef)(null);
const contentRef = (0, _react.useRef)(null);
const [animatedVisible, setAnimatedVisible] = React.useState(visible);
const [isFixedPos, setIsFixedPos] = React.useState(false);
// ========================== Init ==========================
const ariaId = (0, _useId.default)();
function saveLastOutSideActiveElementRef() {
if (!(0, _contains.default)(wrapperRef.current, document.activeElement)) {
lastOutSideActiveElementRef.current = document.activeElement;
}
}
function focusDialogContent() {
if (!(0, _contains.default)(wrapperRef.current, document.activeElement)) {
contentRef.current?.focus();
}
}
// ========================= Events =========================
// Close action will trigger by:
// 1. When hide motion end
// 2. Controlled `open` to `false` immediately after set to `true` which will not trigger motion
function doClose() {
// Clean up scroll bar & focus back
setAnimatedVisible(false);
if (mask && lastOutSideActiveElementRef.current && focusTriggerAfterClose) {
try {
lastOutSideActiveElementRef.current.focus({
preventScroll: true
});
} catch (e) {
// Do nothing
}
lastOutSideActiveElementRef.current = null;
}
// Trigger afterClose only when change visible from true to false
if (animatedVisible) {
afterClose?.();
}
}
function onDialogVisibleChanged(newVisible) {
// Try to focus
if (newVisible) {
focusDialogContent();
} else {
doClose();
}
afterOpenChange?.(newVisible);
}
function onInternalClose(e) {
onClose?.(e);
}
// >>> Content
const mouseDownOnMaskRef = (0, _react.useRef)(false);
// >>> Wrapper
// Close only when element not on dialog
let onWrapperClick = null;
if (maskClosable) {
onWrapperClick = e => {
if (wrapperRef.current === e.target && mouseDownOnMaskRef.current) {
onInternalClose(e);
}
};
}
function onWrapperMouseDown(e) {
mouseDownOnMaskRef.current = e.target === wrapperRef.current;
}
// ========================= Effect =========================
(0, _react.useEffect)(() => {
if (visible) {
mouseDownOnMaskRef.current = false;
setAnimatedVisible(true);
saveLastOutSideActiveElementRef();
// Calc the position style
if (wrapperRef.current) {
const computedWrapStyle = getComputedStyle(wrapperRef.current);
setIsFixedPos(computedWrapStyle.position === 'fixed');
}
} else if (animatedVisible && contentRef.current.enableMotion() && !contentRef.current.inMotion()) {
doClose();
}
}, [visible]);
const mergedStyle = {
zIndex,
...wrapStyle,
...modalStyles?.wrapper,
display: !animatedVisible ? 'none' : null
};
// ========================= Render =========================
return /*#__PURE__*/React.createElement("div", _extends({
className: (0, _clsx.clsx)(`${prefixCls}-root`, rootClassName),
style: rootStyle
}, (0, _pickAttrs.default)(props, {
data: true
})), /*#__PURE__*/React.createElement(_Mask.default, {
prefixCls: prefixCls,
visible: mask && visible,
motionName: (0, _util.getMotionName)(prefixCls, maskTransitionName, maskAnimation),
style: {
zIndex,
...maskStyle,
...modalStyles?.mask
},
maskProps: maskProps,
className: modalClassNames?.mask
}), /*#__PURE__*/React.createElement("div", _extends({
className: (0, _clsx.clsx)(`${prefixCls}-wrap`, wrapClassName, modalClassNames?.wrapper),
ref: wrapperRef,
onClick: onWrapperClick,
onMouseDown: onWrapperMouseDown,
style: mergedStyle
}, wrapProps), /*#__PURE__*/React.createElement(_Content.default, _extends({}, props, {
isFixedPos: isFixedPos,
ref: contentRef,
closable: closable,
ariaId: ariaId,
prefixCls: prefixCls,
visible: visible && animatedVisible,
onClose: onInternalClose,
onVisibleChanged: onDialogVisibleChanged,
motionName: (0, _util.getMotionName)(prefixCls, transitionName, animation)
}))));
};
var _default = exports.default = Dialog;
@@ -0,0 +1,4 @@
import * as React from 'react';
import type { IDialogPropTypes } from './IDialogPropTypes';
declare const DialogWrap: React.FC<IDialogPropTypes>;
export default DialogWrap;
+84
View File
@@ -0,0 +1,84 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _portal = _interopRequireDefault(require("@rc-component/portal"));
var React = _interopRequireWildcard(require("react"));
var _context = require("./context");
var _Dialog = _interopRequireDefault(require("./Dialog"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
// fix issue #10656
/*
* getContainer remarks
* Custom container should not be return, because in the Portal component, it will remove the
* return container element here, if the custom container is the only child of it's component,
* like issue #10656, It will has a conflict with removeChild method in react-dom.
* So here should add a child (div element) to custom container.
* */
const DialogWrap = props => {
const {
visible,
getContainer,
forceRender,
destroyOnHidden = false,
afterClose,
closable,
panelRef,
keyboard = true,
onClose
} = props;
const [animatedVisible, setAnimatedVisible] = React.useState(visible);
const refContext = React.useMemo(() => ({
panel: panelRef
}), [panelRef]);
const onEsc = ({
top,
event
}) => {
if (top && keyboard) {
event.stopPropagation();
onClose?.(event);
return;
}
};
React.useEffect(() => {
if (visible) {
setAnimatedVisible(true);
}
}, [visible]);
// Destroy on close will remove wrapped div
if (!forceRender && destroyOnHidden && !animatedVisible) {
return null;
}
return /*#__PURE__*/React.createElement(_context.RefContext.Provider, {
value: refContext
}, /*#__PURE__*/React.createElement(_portal.default, {
open: visible || forceRender || animatedVisible,
onEsc: onEsc,
autoDestroy: false,
getContainer: getContainer,
autoLock: visible || animatedVisible
}, /*#__PURE__*/React.createElement(_Dialog.default, _extends({}, props, {
destroyOnHidden: destroyOnHidden,
afterClose: () => {
const closableObj = closable && typeof closable === 'object' ? closable : {};
const {
afterClose: closableAfterClose
} = closableObj || {};
closableAfterClose?.();
afterClose?.();
setAnimatedVisible(false);
}
}))));
};
if (process.env.NODE_ENV !== 'production') {
DialogWrap.displayName = 'Dialog';
}
var _default = exports.default = DialogWrap;
@@ -0,0 +1,56 @@
import type { GetContainer } from '@rc-component/util/lib/PortalWrapper';
import type { CSSProperties, ReactNode, SyntheticEvent } from 'react';
export type SemanticName = 'header' | 'body' | 'footer' | 'container' | 'title' | 'wrapper' | 'mask';
export type ModalClassNames = Partial<Record<SemanticName, string>>;
export type ModalStyles = Partial<Record<SemanticName, CSSProperties>>;
export type ClosableType = {
closeIcon?: React.ReactNode;
disabled?: boolean;
afterClose?: () => any;
};
export type IDialogPropTypes = {
className?: string;
keyboard?: boolean;
style?: CSSProperties;
rootStyle?: CSSProperties;
mask?: boolean;
children?: React.ReactNode;
afterClose?: () => any;
afterOpenChange?: (open: boolean) => void;
onClose?: (e: SyntheticEvent | KeyboardEvent) => any;
closable?: boolean | (ClosableType & React.AriaAttributes);
maskClosable?: boolean;
visible?: boolean;
destroyOnHidden?: boolean;
mousePosition?: {
x: number;
y: number;
} | null;
title?: ReactNode;
footer?: ReactNode;
transitionName?: string;
maskTransitionName?: string;
animation?: any;
maskAnimation?: any;
wrapStyle?: Record<string, any>;
bodyStyle?: Record<string, any>;
maskStyle?: Record<string, any>;
prefixCls?: string;
wrapClassName?: string;
width?: string | number;
height?: string | number;
zIndex?: number;
bodyProps?: any;
maskProps?: any;
rootClassName?: string;
classNames?: ModalClassNames;
styles?: ModalStyles;
wrapProps?: any;
getContainer?: GetContainer | false;
closeIcon?: ReactNode;
modalRender?: (node: ReactNode) => ReactNode;
forceRender?: boolean;
focusTriggerAfterClose?: boolean;
focusTrap?: boolean;
panelRef?: React.Ref<HTMLDivElement>;
};
@@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
+5
View File
@@ -0,0 +1,5 @@
import * as React from 'react';
export interface RefContextProps {
panel?: React.Ref<HTMLDivElement>;
}
export declare const RefContext: React.Context<RefContextProps>;
+10
View File
@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.RefContext = void 0;
var React = _interopRequireWildcard(require("react"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
const RefContext = exports.RefContext = /*#__PURE__*/React.createContext({});
+6
View File
@@ -0,0 +1,6 @@
import DialogWrap from './DialogWrap';
import Panel from './Dialog/Content/Panel';
import type { IDialogPropTypes as DialogProps } from './IDialogPropTypes';
export type { DialogProps };
export { Panel };
export default DialogWrap;
+16
View File
@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "Panel", {
enumerable: true,
get: function () {
return _Panel.default;
}
});
exports.default = void 0;
var _DialogWrap = _interopRequireDefault(require("./DialogWrap"));
var _Panel = _interopRequireDefault(require("./Dialog/Content/Panel"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _default = exports.default = _DialogWrap.default;
+5
View File
@@ -0,0 +1,5 @@
export declare function getMotionName(prefixCls: string, transitionName?: string, animationName?: string): string;
export declare function offset(el: Element): {
left: number;
top: number;
};
+41
View File
@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getMotionName = getMotionName;
exports.offset = offset;
// =============================== Motion ===============================
function getMotionName(prefixCls, transitionName, animationName) {
let motionName = transitionName;
if (!motionName && animationName) {
motionName = `${prefixCls}-${animationName}`;
}
return motionName;
}
// =============================== Offset ===============================
function getScroll(w, top) {
let ret = w[`page${top ? 'Y' : 'X'}Offset`];
const method = `scroll${top ? 'Top' : 'Left'}`;
if (typeof ret !== 'number') {
const d = w.document;
ret = d.documentElement[method];
if (typeof ret !== 'number') {
ret = d.body[method];
}
}
return ret;
}
function offset(el) {
const rect = el.getBoundingClientRect();
const pos = {
left: rect.left,
top: rect.top
};
const doc = el.ownerDocument;
const w = doc.defaultView || doc.parentWindow;
pos.left += getScroll(w);
pos.top += getScroll(w, true);
return pos;
}
+90
View File
@@ -0,0 +1,90 @@
{
"name": "@rc-component/dialog",
"version": "1.8.4",
"description": "dialog ui component for react",
"keywords": [
"react",
"react-component",
"react-dialog",
"dialog",
"ui"
],
"homepage": "http://github.com/react-component/dialog",
"bugs": {
"url": "http://github.com/react-component/dialog/issues"
},
"repository": {
"type": "git",
"url": "git@github.com:react-component/dialog.git"
},
"license": "MIT",
"author": "yiminghe@gmail.com",
"main": "./lib/index",
"module": "./es/index",
"files": [
"lib",
"es",
"assets/*.css"
],
"scripts": {
"compile": "father build && lessc assets/index.less assets/index.css && lessc assets/bootstrap.less assets/bootstrap.css",
"coverage": "rc-test --coverage",
"deploy": "npm run docs:build && npm run docs:deploy",
"docs:build": "dumi build",
"docs:deploy": "gh-pages -d dist",
"lint": "eslint src/ --ext .ts,.tsx,.jsx,.js,.md",
"lint:tsc": "tsc -p tsconfig.json --noEmit",
"now-build": "npm run docs:build",
"prepare": "husky install",
"prepublishOnly": "npm run compile && rc-np",
"prettier": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
"start": "dumi dev",
"test": "rc-test",
"tsc": "bunx tsc --noEmit"
},
"lint-staged": {
"**/*.{js,jsx,tsx,ts,md,json}": [
"prettier --write",
"git add"
]
},
"dependencies": {
"@rc-component/motion": "^1.1.3",
"@rc-component/portal": "^2.1.0",
"@rc-component/util": "^1.9.0",
"clsx": "^2.1.1"
},
"devDependencies": {
"@rc-component/drawer": "^1.0.0",
"@rc-component/father-plugin": "^2.0.2",
"@rc-component/np": "^1.0.3",
"@rc-component/select": "^1.0.0",
"@testing-library/jest-dom": "^6.1.6",
"@testing-library/react": "^16.3.0",
"@types/jest": "^30.0.0",
"@types/keyv": "3.1.4",
"@types/node": "^24.0.8",
"@types/react": "^19.1.4",
"@types/react-dom": "^19.1.5",
"@umijs/fabric": "^4.0.0",
"bootstrap": "^5.3.7",
"dumi": "^2.1.3",
"eslint": "8.x",
"father": "^4.1.5",
"gh-pages": "^6.1.1",
"glob": "^11.0.0",
"husky": "^9.1.6",
"less": "^4.1.3",
"lint-staged": "^16.1.2",
"prettier": "^3.2.1",
"rc-test": "^7.0.14",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-draggable": "^4.4.3",
"typescript": "^5.4.3"
},
"peerDependencies": {
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
}