Skip to content

Commit 79649e1

Browse files
authored
feat: export dialog panel (#291)
* feat: export dialog panel * fix: props pass
1 parent 3c2a9d2 commit 79649e1

File tree

6 files changed

+169
-102
lines changed

6 files changed

+169
-102
lines changed

docs/demo/pure.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## pure-debug
2+
3+
<code src="../examples/pure.tsx">

docs/examples/pure.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* eslint no-console:0 */
2+
import * as React from 'react';
3+
import 'rc-select/assets/index.less';
4+
import { Panel } from 'rc-dialog';
5+
import '../../assets/index.less';
6+
7+
export default () => (
8+
<Panel prefixCls="rc-dialog" title="Title" closable>
9+
Hello World!
10+
</Panel>
11+
);

src/Dialog/Content/Panel.tsx

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import React, { useRef } from 'react';
2+
import classNames from 'classnames';
3+
import type { IDialogChildProps } from '..';
4+
import MemoChildren from './MemoChildren';
5+
6+
const sentinelStyle = { width: 0, height: 0, overflow: 'hidden', outline: 'none' };
7+
8+
export interface PanelProps extends Omit<IDialogChildProps, 'getOpenCount'> {
9+
prefixCls: string;
10+
ariaId?: string;
11+
onMouseDown?: React.MouseEventHandler;
12+
onMouseUp?: React.MouseEventHandler;
13+
holderRef?: React.Ref<HTMLDivElement>;
14+
}
15+
16+
export type ContentRef = {
17+
focus: () => void;
18+
changeActive: (next: boolean) => void;
19+
};
20+
21+
const Panel = React.forwardRef<ContentRef, PanelProps>((props, ref) => {
22+
const {
23+
prefixCls,
24+
className,
25+
style,
26+
title,
27+
ariaId,
28+
footer,
29+
closable,
30+
closeIcon,
31+
onClose,
32+
children,
33+
bodyStyle,
34+
bodyProps,
35+
modalRender,
36+
onMouseDown,
37+
onMouseUp,
38+
holderRef,
39+
visible,
40+
forceRender,
41+
width,
42+
height,
43+
} = props;
44+
45+
// ================================= Refs =================================
46+
const sentinelStartRef = useRef<HTMLDivElement>();
47+
const sentinelEndRef = useRef<HTMLDivElement>();
48+
49+
React.useImperativeHandle(ref, () => ({
50+
focus: () => {
51+
sentinelStartRef.current?.focus();
52+
},
53+
changeActive: (next) => {
54+
const { activeElement } = document;
55+
if (next && activeElement === sentinelEndRef.current) {
56+
sentinelStartRef.current.focus();
57+
} else if (!next && activeElement === sentinelStartRef.current) {
58+
sentinelEndRef.current.focus();
59+
}
60+
},
61+
}));
62+
63+
// ================================ Style =================================
64+
const contentStyle: React.CSSProperties = {};
65+
66+
if (width !== undefined) {
67+
contentStyle.width = width;
68+
}
69+
if (height !== undefined) {
70+
contentStyle.height = height;
71+
}
72+
// ================================ Render ================================
73+
let footerNode: React.ReactNode;
74+
if (footer) {
75+
footerNode = <div className={`${prefixCls}-footer`}>{footer}</div>;
76+
}
77+
78+
let headerNode: React.ReactNode;
79+
if (title) {
80+
headerNode = (
81+
<div className={`${prefixCls}-header`}>
82+
<div className={`${prefixCls}-title`} id={ariaId}>
83+
{title}
84+
</div>
85+
</div>
86+
);
87+
}
88+
89+
let closer: React.ReactNode;
90+
if (closable) {
91+
closer = (
92+
<button type="button" onClick={onClose} aria-label="Close" className={`${prefixCls}-close`}>
93+
{closeIcon || <span className={`${prefixCls}-close-x`} />}
94+
</button>
95+
);
96+
}
97+
98+
const content = (
99+
<div className={`${prefixCls}-content`}>
100+
{closer}
101+
{headerNode}
102+
<div className={`${prefixCls}-body`} style={bodyStyle} {...bodyProps}>
103+
{children}
104+
</div>
105+
{footerNode}
106+
</div>
107+
);
108+
109+
return (
110+
<div
111+
key="dialog-element"
112+
role="dialog"
113+
aria-labelledby={title ? ariaId : null}
114+
aria-modal="true"
115+
ref={holderRef}
116+
style={{
117+
...style,
118+
...contentStyle,
119+
}}
120+
className={classNames(prefixCls, className)}
121+
onMouseDown={onMouseDown}
122+
onMouseUp={onMouseUp}
123+
>
124+
<div tabIndex={0} ref={sentinelStartRef} style={sentinelStyle} aria-hidden="true" />
125+
<MemoChildren shouldUpdate={visible || forceRender}>
126+
{modalRender ? modalRender(content) : content}
127+
</MemoChildren>
128+
<div tabIndex={0} ref={sentinelEndRef} style={sentinelStyle} aria-hidden="true" />
129+
</div>
130+
);
131+
});
132+
133+
if (process.env.NODE_ENV !== 'production') {
134+
Panel.displayName = 'Panel';
135+
}
136+
137+
export default Panel;

src/Dialog/Content/index.tsx

Lines changed: 13 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -2,80 +2,37 @@ import * as React from 'react';
22
import { useRef } from 'react';
33
import classNames from 'classnames';
44
import CSSMotion from 'rc-motion';
5-
import type { IDialogChildProps } from '..';
65
import { offset } from '../../util';
7-
import MemoChildren from './MemoChildren';
8-
9-
const sentinelStyle = { width: 0, height: 0, overflow: 'hidden', outline: 'none' };
6+
import type { PanelProps, ContentRef } from './Panel';
7+
import Panel from './Panel';
108

119
export type ContentProps = {
1210
motionName: string;
1311
ariaId: string;
1412
onVisibleChanged: (visible: boolean) => void;
15-
onMouseDown: React.MouseEventHandler;
16-
onMouseUp: React.MouseEventHandler;
17-
} & IDialogChildProps;
18-
19-
export type ContentRef = {
20-
focus: () => void;
21-
changeActive: (next: boolean) => void;
22-
};
13+
} & PanelProps;
2314

2415
const Content = React.forwardRef<ContentRef, ContentProps>((props, ref) => {
2516
const {
26-
closable,
2717
prefixCls,
28-
width,
29-
height,
30-
footer,
3118
title,
32-
closeIcon,
3319
style,
3420
className,
3521
visible,
3622
forceRender,
37-
bodyStyle,
38-
bodyProps,
39-
children,
4023
destroyOnClose,
41-
modalRender,
4224
motionName,
4325
ariaId,
44-
onClose,
4526
onVisibleChanged,
46-
onMouseDown,
47-
onMouseUp,
4827
mousePosition,
4928
} = props;
5029

51-
const sentinelStartRef = useRef<HTMLDivElement>();
52-
const sentinelEndRef = useRef<HTMLDivElement>();
5330
const dialogRef = useRef<HTMLDivElement>();
5431

55-
// ============================== Ref ===============================
56-
React.useImperativeHandle(ref, () => ({
57-
focus: () => {
58-
sentinelStartRef.current?.focus();
59-
},
60-
changeActive: (next) => {
61-
const { activeElement } = document;
62-
if (next && activeElement === sentinelEndRef.current) {
63-
sentinelStartRef.current.focus();
64-
} else if (!next && activeElement === sentinelStartRef.current) {
65-
sentinelEndRef.current.focus();
66-
}
67-
},
68-
}));
69-
7032
// ============================= Style ==============================
7133
const [transformOrigin, setTransformOrigin] = React.useState<string>();
7234
const contentStyle: React.CSSProperties = {};
73-
if (width !== undefined) {
74-
contentStyle.width = width;
75-
}
76-
if (height !== undefined) {
77-
contentStyle.height = height;
78-
}
35+
7936
if (transformOrigin) {
8037
contentStyle.transformOrigin = transformOrigin;
8138
}
@@ -91,42 +48,6 @@ const Content = React.forwardRef<ContentRef, ContentProps>((props, ref) => {
9148
}
9249

9350
// ============================= Render =============================
94-
let footerNode: React.ReactNode;
95-
if (footer) {
96-
footerNode = <div className={`${prefixCls}-footer`}>{footer}</div>;
97-
}
98-
99-
let headerNode: React.ReactNode;
100-
if (title) {
101-
headerNode = (
102-
<div className={`${prefixCls}-header`}>
103-
<div className={`${prefixCls}-title`} id={ariaId}>
104-
{title}
105-
</div>
106-
</div>
107-
);
108-
}
109-
110-
let closer: React.ReactNode;
111-
if (closable) {
112-
closer = (
113-
<button type="button" onClick={onClose} aria-label="Close" className={`${prefixCls}-close`}>
114-
{closeIcon || <span className={`${prefixCls}-close-x`} />}
115-
</button>
116-
);
117-
}
118-
119-
const content = (
120-
<div className={`${prefixCls}-content`}>
121-
{closer}
122-
{headerNode}
123-
<div className={`${prefixCls}-body`} style={bodyStyle} {...bodyProps}>
124-
{children}
125-
</div>
126-
{footerNode}
127-
</div>
128-
);
129-
13051
return (
13152
<CSSMotion
13253
visible={visible}
@@ -139,23 +60,16 @@ const Content = React.forwardRef<ContentRef, ContentProps>((props, ref) => {
13960
ref={dialogRef}
14061
>
14162
{({ className: motionClassName, style: motionStyle }, motionRef) => (
142-
<div
143-
key="dialog-element"
144-
role="dialog"
145-
aria-labelledby={title ? ariaId : null}
146-
aria-modal="true"
147-
ref={motionRef}
63+
<Panel
64+
{...props}
65+
ref={ref}
66+
title={title}
67+
ariaId={ariaId}
68+
prefixCls={prefixCls}
69+
holderRef={motionRef}
14870
style={{ ...motionStyle, ...style, ...contentStyle }}
149-
className={classNames(prefixCls, className, motionClassName)}
150-
onMouseDown={onMouseDown}
151-
onMouseUp={onMouseUp}
152-
>
153-
<div tabIndex={0} ref={sentinelStartRef} style={sentinelStyle} aria-hidden="true" />
154-
<MemoChildren shouldUpdate={visible || forceRender}>
155-
{modalRender ? modalRender(content) : content}
156-
</MemoChildren>
157-
<div tabIndex={0} ref={sentinelEndRef} style={sentinelStyle} aria-hidden="true" />
158-
</div>
71+
className={classNames(className, motionClassName)}
72+
/>
15973
)}
16074
</CSSMotion>
16175
);

src/Dialog/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import type ScollLocker from 'rc-util/lib/Dom/scrollLocker';
99
import type { IDialogPropTypes } from '../IDialogPropTypes';
1010
import Mask from './Mask';
1111
import { getMotionName } from '../util';
12-
import type { ContentRef } from './Content';
1312
import Content from './Content';
13+
import type { ContentRef } from './Content/Panel';
1414

1515
export type IDialogChildProps = {
1616
// zombieJ: This should be handle on top instead of each Dialog.

src/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import DialogWrap from './DialogWrap';
2-
import { IDialogPropTypes as DialogProps } from './IDialogPropTypes';
2+
import Panel from './Dialog/Content/Panel';
3+
import type { IDialogPropTypes as DialogProps } from './IDialogPropTypes';
34

4-
export { DialogProps };
5+
export type { DialogProps };
6+
export { Panel };
57

68
export default DialogWrap;

0 commit comments

Comments
 (0)