Skip to content

Commit 3f0ed59

Browse files
committed
Merge remote-tracking branch 'origin/master' into semantic-close
2 parents b7e7636 + 045d38c commit 3f0ed59

15 files changed

Lines changed: 408 additions & 260 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,4 @@ pnpm-lock.yaml
5050
.dumi/tmp-production
5151

5252
bun.lockb
53+
.vscode

README.md

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
1-
# rc-dialog
1+
# @rc-component/dialog
22

3-
react dialog component
3+
react dialog component.
44

5-
[![NPM version][npm-image]][npm-url] [![dumi](https://img.shields.io/badge/docs%20by-dumi-blue?style=flat-square)](https://github.com/umijs/dumi) [![build status][github-actions-image]][github-actions-url] [![Test coverage][codecov-image]][codecov-url] [![npm download][download-image]][download-url] [![bundle size][bundlephobia-image]][bundlephobia-url]
5+
[![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]
66

7-
[npm-image]: http://img.shields.io/npm/v/rc-dialog.svg?style=flat-square
8-
[npm-url]: http://npmjs.org/package/rc-dialog
9-
[github-actions-image]: https://github.com/react-component/dialog/workflows/CI/badge.svg
10-
[github-actions-url]: https://github.com/react-component/dialog/actions
11-
[circleci-image]: https://img.shields.io/circleci/react-component/dialog/master?style=flat-square
12-
[circleci-url]: https://circleci.com/gh/react-component/dialog
7+
[npm-image]: https://img.shields.io/npm/v/@rc-component/dialog.svg?style=flat-square
8+
[npm-url]: https://npmjs.org/package/@rc-component/dialog
9+
[travis-image]: https://img.shields.io/travis/react-component/dialog/master?style=flat-square
10+
[travis-url]: https://travis-ci.com/react-component/dialog
11+
[github-actions-image]: https://github.com/react-component/dialog/actions/workflows/react-component-ci.yml/badge.svg
12+
[github-actions-url]: https://github.com/react-component/dialog/actions/workflows/react-component-ci.yml
1313
[codecov-image]: https://img.shields.io/codecov/c/github/react-component/dialog/master.svg?style=flat-square
1414
[codecov-url]: https://app.codecov.io/gh/react-component/dialog
15-
[download-image]: https://img.shields.io/npm/dm/rc-dialog.svg?style=flat-square
16-
[download-url]: https://npmjs.org/package/rc-dialog
17-
[bundlephobia-url]: https://bundlephobia.com/result?p=rc-dialog
18-
[bundlephobia-image]: https://badgen.net/bundlephobia/minzip/rc-dialog
15+
[david-url]: https://david-dm.org/react-component/dialog
16+
[david-image]: https://david-dm.org/react-component/dialog/status.svg?style=flat-square
17+
[david-dev-url]: https://david-dm.org/react-component/dialog?type=dev
18+
[david-dev-image]: https://david-dm.org/react-component/dialog/dev-status.svg?style=flat-square
19+
[download-image]: https://img.shields.io/npm/dm/@rc-component/dialog.svg?style=flat-square
20+
[download-url]: https://npmjs.org/package/@rc-component/dialog
21+
[bundlephobia-url]: https://bundlephobia.com/package/@rc-component/dialog
22+
[bundlephobia-image]: https://badgen.net/bundlephobia/minzip/@rc-component/dialog
23+
[dumi-url]: https://github.com/umijs/dumi
24+
[dumi-image]: https://img.shields.io/badge/docs%20by-dumi-blue?style=flat-square
25+
1926

2027
## Screenshot
2128

@@ -29,12 +36,12 @@ online example: https://dialog.react-component.vercel.app/
2936

3037
## Install
3138

32-
[![rc-dialog](https://nodei.co/npm/rc-dialog.png)](https://npmjs.org/package/rc-dialog)
39+
[![@rc-component/dialog](https://nodei.co/npm/@rc-component/dialog.png)](https://npmjs.org/package/@rc-component/dialog)
3340

3441
## Usage
3542

3643
```js
37-
var Dialog = require('rc-dialog');
44+
var Dialog = require('@rc-component/dialog');
3845

3946
ReactDOM.render(
4047
<Dialog title={title} onClose={callback1} visible>
@@ -47,7 +54,7 @@ ReactDOM.render(
4754

4855
## API
4956

50-
### rc-dialog
57+
### @rc-component/dialog
5158

5259
| Name | Type | Default | Description | Version |
5360
| --- | --- | --- | --- | --- |
@@ -102,7 +109,7 @@ open coverage/ dir
102109

103110
## License
104111

105-
rc-dialog is released under the MIT license.
112+
@rc-component/dialog is released under the MIT license.
106113

107114

108115
## 🤝 Contributing

docs/demo/with-portal.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: with-portal
3+
nav:
4+
title: Demo
5+
path: /demo
6+
---
7+
8+
<code src="../examples/with-portal.tsx"></code>

docs/examples/with-portal.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import Dialog from '@rc-component/dialog';
4+
5+
const DivPortal: React.FC = () => {
6+
return ReactDOM.createPortal(
7+
<div
8+
id="test-portal"
9+
style={{
10+
position: 'fixed',
11+
right: 20,
12+
bottom: 20,
13+
background: 'white',
14+
padding: 20,
15+
border: '1px solid #ccc',
16+
zIndex: 1000000,
17+
}}
18+
>
19+
<input type="text" />
20+
</div>,
21+
document.body
22+
);
23+
};
24+
25+
const MyControl: React.FC = () => {
26+
const [visible, setVisible] = React.useState(false);
27+
28+
const onClick = () => {
29+
setVisible(true);
30+
};
31+
32+
const onClose = () => {
33+
setVisible(false);
34+
};
35+
36+
return (
37+
<div style={{ margin: 20 }}>
38+
<p>
39+
<button type="button" onClick={onClick}>
40+
show dialog
41+
</button>
42+
</p>
43+
<Dialog visible={visible} onClose={onClose}>
44+
hello world
45+
<input type="text" />
46+
<input type="text" />
47+
<DivPortal />
48+
</Dialog>
49+
</div>
50+
);
51+
};
52+
53+
export default MyControl;

package.json

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rc-component/dialog",
3-
"version": "1.5.0",
3+
"version": "1.8.4",
44
"description": "dialog ui component for react",
55
"keywords": [
66
"react",
@@ -24,8 +24,7 @@
2424
"files": [
2525
"lib",
2626
"es",
27-
"assets/*.css",
28-
"dist"
27+
"assets/*.css"
2928
],
3029
"scripts": {
3130
"compile": "father build && lessc assets/index.less assets/index.css && lessc assets/bootstrap.less assets/bootstrap.css",
@@ -51,8 +50,8 @@
5150
},
5251
"dependencies": {
5352
"@rc-component/motion": "^1.1.3",
54-
"@rc-component/portal": "^2.0.0",
55-
"@rc-component/util": "^1.0.1",
53+
"@rc-component/portal": "^2.1.0",
54+
"@rc-component/util": "^1.9.0",
5655
"clsx": "^2.1.1"
5756
},
5857
"devDependencies": {
@@ -62,18 +61,15 @@
6261
"@rc-component/select": "^1.0.0",
6362
"@testing-library/jest-dom": "^6.1.6",
6463
"@testing-library/react": "^16.3.0",
65-
"@types/jest": "^29.4.0",
64+
"@types/jest": "^30.0.0",
6665
"@types/keyv": "3.1.4",
6766
"@types/node": "^24.0.8",
6867
"@types/react": "^19.1.4",
6968
"@types/react-dom": "^19.1.5",
70-
"@umijs/fabric": "^3.0.0",
69+
"@umijs/fabric": "^4.0.0",
7170
"bootstrap": "^5.3.7",
72-
"cross-env": "^7.0.0",
7371
"dumi": "^2.1.3",
74-
"eslint": "^7.1.0",
75-
"eslint-config-airbnb": "^19.0.4",
76-
"eslint-plugin-react": "^7.20.6",
72+
"eslint": "8.x",
7773
"father": "^4.1.5",
7874
"gh-pages": "^6.1.1",
7975
"glob": "^11.0.0",
@@ -91,4 +87,4 @@
9187
"react": ">=18.0.0",
9288
"react-dom": ">=18.0.0"
9389
}
94-
}
90+
}

src/Dialog/Content/Panel.tsx

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,24 @@
11
import { clsx } from 'clsx';
22
import { useComposeRef } from '@rc-component/util/lib/ref';
3+
import { useLockFocus } from '@rc-component/util/lib/Dom/focus';
34
import React, { useMemo, useRef } from 'react';
45
import { RefContext } from '../../context';
56
import type { IDialogPropTypes } from '../../IDialogPropTypes';
67
import MemoChildren from './MemoChildren';
78
import pickAttrs from '@rc-component/util/lib/pickAttrs';
89

9-
const sentinelStyle: React.CSSProperties = {
10-
width: 0,
11-
height: 0,
12-
overflow: 'hidden',
13-
outline: 'none',
14-
};
15-
16-
const entityStyle: React.CSSProperties = {
17-
outline: 'none',
18-
};
19-
2010
export interface PanelProps extends Omit<IDialogPropTypes, 'getOpenCount'> {
2111
prefixCls: string;
2212
ariaId?: string;
2313
onMouseDown?: React.MouseEventHandler;
2414
onMouseUp?: React.MouseEventHandler;
2515
holderRef?: React.Ref<HTMLDivElement>;
16+
/** Used for focus lock. When true and open, focus will lock into the panel */
17+
isFixedPos?: boolean;
2618
}
2719

2820
export type PanelRef = {
2921
focus: () => void;
30-
changeActive: (next: boolean) => void;
3122
};
3223

3324
const Panel = React.forwardRef<PanelRef, PanelProps>((props, ref) => {
@@ -54,27 +45,23 @@ const Panel = React.forwardRef<PanelRef, PanelProps>((props, ref) => {
5445
height,
5546
classNames: modalClassNames,
5647
styles: modalStyles,
48+
isFixedPos,
49+
focusTrap,
5750
} = props;
5851

5952
// ================================= Refs =================================
6053
const { panel: panelRef } = React.useContext(RefContext);
54+
const internalRef = useRef<HTMLDivElement>(null);
55+
const mergedRef = useComposeRef(holderRef, panelRef, internalRef);
6156

62-
const mergedRef = useComposeRef(holderRef, panelRef);
63-
64-
const sentinelStartRef = useRef<HTMLDivElement>(null);
65-
const sentinelEndRef = useRef<HTMLDivElement>(null);
57+
const [ignoreElement] = useLockFocus(
58+
visible && isFixedPos && focusTrap !== false,
59+
() => internalRef.current,
60+
);
6661

6762
React.useImperativeHandle(ref, () => ({
6863
focus: () => {
69-
sentinelStartRef.current?.focus({ preventScroll: true });
70-
},
71-
changeActive: (next) => {
72-
const { activeElement } = document;
73-
if (next && activeElement === sentinelEndRef.current) {
74-
sentinelStartRef.current.focus({ preventScroll: true });
75-
} else if (!next && activeElement === sentinelStartRef.current) {
76-
sentinelEndRef.current.focus({ preventScroll: true });
77-
}
64+
internalRef.current?.focus({ preventScroll: true });
7865
},
7966
}));
8067

@@ -168,13 +155,14 @@ const Panel = React.forwardRef<PanelRef, PanelProps>((props, ref) => {
168155
className={clsx(prefixCls, className)}
169156
onMouseDown={onMouseDown}
170157
onMouseUp={onMouseUp}
158+
tabIndex={-1}
159+
onFocus={(e) => {
160+
ignoreElement(e.target);
161+
}}
171162
>
172-
<div ref={sentinelStartRef} tabIndex={0} style={entityStyle}>
173-
<MemoChildren shouldUpdate={visible || forceRender}>
174-
{modalRender ? modalRender(content) : content}
175-
</MemoChildren>
176-
</div>
177-
<div tabIndex={0} ref={sentinelEndRef} style={sentinelStyle} />
163+
<MemoChildren shouldUpdate={visible || forceRender}>
164+
{modalRender ? modalRender(content) : content}
165+
</MemoChildren>
178166
</div>
179167
);
180168
});

src/Dialog/Content/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ const Content = React.forwardRef<ContentRef, ContentProps>((props, ref) => {
5252
}
5353

5454
function onPrepare() {
55+
if (!dialogRef.current?.nativeElement) {
56+
return;
57+
}
58+
5559
const elementOffset = offset(dialogRef.current.nativeElement);
5660

5761
setTransformOrigin(

0 commit comments

Comments
 (0)