@jetbrains/ring-ui 6.0.63 → 6.0.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { PureComponent } from 'react';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import PropTypes from 'prop-types';
|
|
4
3
|
import TabTrap, { TabTrapProps } from '../tab-trap/tab-trap';
|
|
5
4
|
import type { ShortcutsScopeOptions } from '../shortcuts/core';
|
|
6
5
|
export interface DialogProps extends Partial<TabTrapProps> {
|
|
@@ -24,26 +23,6 @@ export interface DialogProps extends Partial<TabTrapProps> {
|
|
|
24
23
|
preventBodyScroll?: boolean;
|
|
25
24
|
}
|
|
26
25
|
export default class Dialog extends PureComponent<DialogProps> {
|
|
27
|
-
static propTypes: {
|
|
28
|
-
label: PropTypes.Requireable<string>;
|
|
29
|
-
className: PropTypes.Requireable<string>;
|
|
30
|
-
contentClassName: PropTypes.Requireable<string>;
|
|
31
|
-
children: PropTypes.Requireable<NonNullable<PropTypes.ReactNodeLike>>;
|
|
32
|
-
show: PropTypes.Validator<boolean>;
|
|
33
|
-
showCloseButton: PropTypes.Requireable<boolean>;
|
|
34
|
-
closeButtonInside: PropTypes.Requireable<boolean>;
|
|
35
|
-
closeButtonTitle: PropTypes.Requireable<string>;
|
|
36
|
-
onOverlayClick: PropTypes.Requireable<(...args: any[]) => any>;
|
|
37
|
-
onEscPress: PropTypes.Requireable<(...args: any[]) => any>;
|
|
38
|
-
onCloseClick: PropTypes.Requireable<(...args: any[]) => any>;
|
|
39
|
-
shortcutOptions: PropTypes.Requireable<object>;
|
|
40
|
-
onCloseAttempt: PropTypes.Requireable<(...args: any[]) => any>;
|
|
41
|
-
trapFocus: PropTypes.Requireable<boolean>;
|
|
42
|
-
portalTarget: PropTypes.Requireable<HTMLElement>;
|
|
43
|
-
autoFocusFirst: PropTypes.Requireable<boolean>;
|
|
44
|
-
'data-test': PropTypes.Requireable<string>;
|
|
45
|
-
preventBodyScroll: PropTypes.Requireable<boolean>;
|
|
46
|
-
};
|
|
47
26
|
static defaultProps: Partial<DialogProps>;
|
|
48
27
|
state: {
|
|
49
28
|
shortcutsScope: string;
|
|
@@ -68,4 +47,4 @@ export default class Dialog extends PureComponent<DialogProps> {
|
|
|
68
47
|
nativeDialog: React.RefObject<HTMLDialogElement>;
|
|
69
48
|
render(): false | React.JSX.Element;
|
|
70
49
|
}
|
|
71
|
-
export type DialogAttrs = JSX.LibraryManagedAttributes<typeof Dialog, DialogProps>;
|
|
50
|
+
export type DialogAttrs = React.JSX.LibraryManagedAttributes<typeof Dialog, DialogProps>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createRef, PureComponent } from 'react';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
|
-
import PropTypes from 'prop-types';
|
|
5
4
|
import classNames from 'classnames';
|
|
6
5
|
import closeIcon from '@jetbrains/icons/close';
|
|
7
6
|
import { AdaptiveIsland } from '../island/island';
|
|
@@ -10,7 +9,8 @@ import dataTests from '../global/data-tests';
|
|
|
10
9
|
import Shortcuts from '../shortcuts/shortcuts';
|
|
11
10
|
import TabTrap from '../tab-trap/tab-trap';
|
|
12
11
|
import Button from '../button/button';
|
|
13
|
-
import { PopupTarget } from '../popup/popup.target';
|
|
12
|
+
import { PopupTarget, PopupTargetContext } from '../popup/popup.target';
|
|
13
|
+
import { getPopupContainer } from '../popup/popup';
|
|
14
14
|
import { preventerFactory as scrollPreventerFactory } from './dialog__body-scroll-preventer';
|
|
15
15
|
import styles from './dialog.css';
|
|
16
16
|
/**
|
|
@@ -18,32 +18,6 @@ import styles from './dialog.css';
|
|
|
18
18
|
*/
|
|
19
19
|
function noop() { }
|
|
20
20
|
export default class Dialog extends PureComponent {
|
|
21
|
-
static propTypes = {
|
|
22
|
-
label: PropTypes.string,
|
|
23
|
-
className: PropTypes.string,
|
|
24
|
-
contentClassName: PropTypes.string,
|
|
25
|
-
children: PropTypes.oneOfType([
|
|
26
|
-
PropTypes.arrayOf(PropTypes.node),
|
|
27
|
-
PropTypes.node
|
|
28
|
-
]),
|
|
29
|
-
show: PropTypes.bool.isRequired,
|
|
30
|
-
showCloseButton: PropTypes.bool,
|
|
31
|
-
closeButtonInside: PropTypes.bool,
|
|
32
|
-
closeButtonTitle: PropTypes.string,
|
|
33
|
-
onOverlayClick: PropTypes.func,
|
|
34
|
-
onEscPress: PropTypes.func,
|
|
35
|
-
onCloseClick: PropTypes.func,
|
|
36
|
-
shortcutOptions: PropTypes.object,
|
|
37
|
-
// onCloseAttempt is a common callback for ESC pressing and overlay clicking.
|
|
38
|
-
// Use it if you don't need different behaviors for this cases.
|
|
39
|
-
onCloseAttempt: PropTypes.func,
|
|
40
|
-
// focusTrap may break popups inside dialog, so use it carefully
|
|
41
|
-
trapFocus: PropTypes.bool,
|
|
42
|
-
portalTarget: PropTypes.instanceOf(HTMLElement),
|
|
43
|
-
autoFocusFirst: PropTypes.bool,
|
|
44
|
-
'data-test': PropTypes.string,
|
|
45
|
-
preventBodyScroll: PropTypes.bool
|
|
46
|
-
};
|
|
47
21
|
static defaultProps = {
|
|
48
22
|
label: 'Dialog',
|
|
49
23
|
onOverlayClick: noop,
|
|
@@ -87,7 +61,12 @@ export default class Dialog extends PureComponent {
|
|
|
87
61
|
if (this.nativeDialog.current != null) {
|
|
88
62
|
if (show) {
|
|
89
63
|
this.nativeDialog.current.removeAttribute('open');
|
|
90
|
-
|
|
64
|
+
if (modal) {
|
|
65
|
+
this.nativeDialog.current.showModal();
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.nativeDialog.current.show();
|
|
69
|
+
}
|
|
91
70
|
}
|
|
92
71
|
else {
|
|
93
72
|
this.nativeDialog.current.close();
|
|
@@ -137,30 +116,48 @@ export default class Dialog extends PureComponent {
|
|
|
137
116
|
<Shortcuts map={shortcutsMap} scope={this.state.shortcutsScope} options={this.props.shortcutOptions}/>
|
|
138
117
|
{(onOverlayClick !== noop || onCloseAttempt !== noop) && (<div
|
|
139
118
|
// click handler is duplicated in close button
|
|
140
|
-
role="presentation" className={styles.clickableOverlay} onClick={this.handleClick}/>)}
|
|
119
|
+
role="presentation" className={styles.clickableOverlay} onClick={this.handleClick} data-test="ring-dialog-overlay"/>)}
|
|
141
120
|
<div className={styles.innerContainer}>
|
|
142
|
-
<AdaptiveIsland className={classNames(styles.content, contentClassName, {
|
|
121
|
+
<AdaptiveIsland className={classNames(styles.content, contentClassName, {
|
|
122
|
+
[styles.dense]: dense
|
|
123
|
+
})} data-test="ring-dialog" role="dialog" aria-label={label}>
|
|
143
124
|
{children}
|
|
144
|
-
{showCloseButton &&
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
})} iconClassName={styles.closeIcon} onClick={this.onCloseClick} title={closeButtonTitle} aria-label={closeButtonTitle || 'close dialog'}/>)}
|
|
125
|
+
{showCloseButton && (<Button icon={closeIcon} data-test="ring-dialog-close-button" className={classNames(styles.closeButton, {
|
|
126
|
+
[styles.closeButtonOutside]: !closeButtonInside,
|
|
127
|
+
[styles.closeButtonInside]: closeButtonInside
|
|
128
|
+
})} iconClassName={styles.closeIcon} onClick={this.onCloseClick} title={closeButtonTitle} aria-label={closeButtonTitle || 'close dialog'}/>)}
|
|
149
129
|
</AdaptiveIsland>
|
|
150
130
|
</div>
|
|
151
131
|
</>);
|
|
152
132
|
if (native) {
|
|
153
133
|
return (<dialog className={classNames(styles.nativeDialog, className)} ref={this.nativeDialog}>
|
|
154
134
|
<PopupTarget id={this.uid} className={styles.popupTarget}>
|
|
155
|
-
{target => <>
|
|
135
|
+
{target => (<>
|
|
136
|
+
{content}
|
|
137
|
+
{target}
|
|
138
|
+
</>)}
|
|
156
139
|
</PopupTarget>
|
|
157
140
|
</dialog>);
|
|
158
141
|
}
|
|
159
|
-
return show &&
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
142
|
+
return (show && (<PopupTargetContext.Consumer>
|
|
143
|
+
{contextTarget => {
|
|
144
|
+
let targetElement = document.body;
|
|
145
|
+
if (portalTarget instanceof HTMLElement) {
|
|
146
|
+
targetElement = portalTarget;
|
|
147
|
+
}
|
|
148
|
+
else if (contextTarget != null) {
|
|
149
|
+
const container = getPopupContainer(contextTarget);
|
|
150
|
+
if (container != null) {
|
|
151
|
+
targetElement = container;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return createPortal(<PopupTarget id={this.uid} className={styles.popupTarget}>
|
|
155
|
+
{target => (<TabTrap trapDisabled={!trapFocus} data-test={dataTests('ring-dialog-container', dataTest)} ref={this.dialogRef} className={classes} role="presentation" {...restProps}>
|
|
156
|
+
{content}
|
|
157
|
+
{target}
|
|
158
|
+
</TabTrap>)}
|
|
159
|
+
</PopupTarget>, targetElement);
|
|
160
|
+
}}
|
|
161
|
+
</PopupTargetContext.Consumer>));
|
|
165
162
|
}
|
|
166
163
|
}
|
|
@@ -28,7 +28,8 @@ export default function focusSensorHOC(ComposedComponent) {
|
|
|
28
28
|
}
|
|
29
29
|
componentDidUpdate(prevProps) {
|
|
30
30
|
const { focused } = this.props;
|
|
31
|
-
|
|
31
|
+
const isInFocus = this.node?.contains(document.activeElement);
|
|
32
|
+
if (focused && (!isInFocus || !prevProps.focused)) {
|
|
32
33
|
this.onFocusRestore();
|
|
33
34
|
}
|
|
34
35
|
else if (!focused && prevProps.focused) {
|
|
@@ -53,7 +53,6 @@ export interface PopupProps extends BasePopupProps {
|
|
|
53
53
|
}
|
|
54
54
|
interface PopupState {
|
|
55
55
|
display: Display;
|
|
56
|
-
client?: boolean;
|
|
57
56
|
direction?: Directions;
|
|
58
57
|
}
|
|
59
58
|
/**
|
|
@@ -98,7 +97,6 @@ export default class Popup<P extends BasePopupProps = PopupProps> extends PureCo
|
|
|
98
97
|
uid: string;
|
|
99
98
|
calculateDisplay: (prevState: PopupState) => {
|
|
100
99
|
display: Display;
|
|
101
|
-
client?: boolean;
|
|
102
100
|
direction?: Directions;
|
|
103
101
|
};
|
|
104
102
|
static PopupProps: {
|
|
@@ -120,6 +118,7 @@ export default class Popup<P extends BasePopupProps = PopupProps> extends PureCo
|
|
|
120
118
|
private _redraw;
|
|
121
119
|
private _getAnchor;
|
|
122
120
|
private _listenersEnabled?;
|
|
121
|
+
private _prevTimeout?;
|
|
123
122
|
/**
|
|
124
123
|
* @param {boolean} enable
|
|
125
124
|
* @private
|
|
@@ -71,9 +71,6 @@ export default class Popup extends PureComponent {
|
|
|
71
71
|
display: Display.SHOWING
|
|
72
72
|
};
|
|
73
73
|
componentDidMount() {
|
|
74
|
-
if (!this.props.client) {
|
|
75
|
-
this.setState({ client: true });
|
|
76
|
-
}
|
|
77
74
|
if (!this.props.hidden) {
|
|
78
75
|
this._setListenersEnabled(true);
|
|
79
76
|
}
|
|
@@ -195,13 +192,15 @@ export default class Popup extends PureComponent {
|
|
|
195
192
|
return this.props.anchorElement || this.parent;
|
|
196
193
|
}
|
|
197
194
|
_listenersEnabled;
|
|
195
|
+
_prevTimeout;
|
|
198
196
|
/**
|
|
199
197
|
* @param {boolean} enable
|
|
200
198
|
* @private
|
|
201
199
|
*/
|
|
202
200
|
_setListenersEnabled(enable) {
|
|
203
201
|
if (enable && !this._listenersEnabled) {
|
|
204
|
-
|
|
202
|
+
clearTimeout(this._prevTimeout);
|
|
203
|
+
this._prevTimeout = window.setTimeout(() => {
|
|
205
204
|
this._listenersEnabled = true;
|
|
206
205
|
this.listeners.add(window, 'resize', this._redraw);
|
|
207
206
|
if (this.props.autoPositioningOnScroll) {
|
|
@@ -218,6 +217,7 @@ export default class Popup extends PureComponent {
|
|
|
218
217
|
}
|
|
219
218
|
if (!enable && this._listenersEnabled) {
|
|
220
219
|
this.listeners.removeAll();
|
|
220
|
+
clearTimeout(this._prevTimeout);
|
|
221
221
|
this._listenersEnabled = false;
|
|
222
222
|
}
|
|
223
223
|
}
|
|
@@ -283,7 +283,7 @@ export default class Popup extends PureComponent {
|
|
|
283
283
|
{this.shouldUseShortcuts() &&
|
|
284
284
|
(<Shortcuts map={this.shortcutsMap} scope={this.shortcutsScope}/>)}
|
|
285
285
|
|
|
286
|
-
{
|
|
286
|
+
{client !== false && (keepMounted || !hidden) && createPortal(<PopupTarget id={this.uid} ref={this.containerRef} onMouseOver={onMouseOver} onFocus={onMouseOver} onMouseOut={onMouseOut} onBlur={onMouseOut} onContextMenu={onContextMenu}>
|
|
287
287
|
<div data-test={dataTests('ring-popup', dataTest)} data-test-shown={!hidden && !showing} data-test-direction={direction} ref={this.popupRef} className={classes} style={style} onMouseDown={onMouseDown} onMouseUp={onMouseUp}
|
|
288
288
|
// mouse handlers are used to track clicking on inner elements
|
|
289
289
|
role="presentation">
|