jiti-meet/react/features/base/dialog/components/StatelessDialog.web.js

341 lines
9.3 KiB
JavaScript

import Button, { ButtonGroup } from '@atlaskit/button';
import ModalDialog from '@atlaskit/modal-dialog';
import { AtlasKitThemeProvider } from '@atlaskit/theme';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { translate } from '../../i18n';
import { DIALOG_PROP_TYPES } from '../constants';
/**
* The ID to be used for the cancel button if enabled.
* @type {string}
*/
const CANCEL_BUTTON_ID = 'modal-dialog-cancel-button';
/**
* The ID to be used for the ok button if enabled.
* @type {string}
*/
const OK_BUTTON_ID = 'modal-dialog-ok-button';
/**
* Web dialog that uses atlaskit modal-dialog to display dialogs.
*/
class StatelessDialog extends Component {
/**
* {@code StatelessDialog} component's property types.
*
* @static
*/
static propTypes = {
...DIALOG_PROP_TYPES,
/**
* This is the body of the dialog, the component children.
*/
children: PropTypes.node,
/**
* Disables dismissing the dialog when the blanket is clicked. Enabled
* by default.
*/
disableBlanketClickDismiss: PropTypes.bool,
/**
* Whether the dialog is modal. This means clicking on the blanket will
* leave the dialog open. No cancel button.
*/
isModal: PropTypes.bool,
/**
* Disables rendering of the submit button.
*/
submitDisabled: PropTypes.bool,
/**
* Width of the dialog, can be:
* - 'small' (400px), 'medium' (600px), 'large' (800px),
* 'x-large' (968px)
* - integer value for pixel width
* - string value for percentage
*/
width: PropTypes.string
};
/**
* Initializes a new {@code StatelessDialog} instance.
*
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.
*/
constructor(props) {
super(props);
// Bind event handlers so they are only bound once for every instance.
this._onCancel = this._onCancel.bind(this);
this._onDialogDismissed = this._onDialogDismissed.bind(this);
this._onKeyDown = this._onKeyDown.bind(this);
this._onSubmit = this._onSubmit.bind(this);
this._setDialogElement = this._setDialogElement.bind(this);
}
/**
* React Component method that executes once component is mounted.
*
* @inheritdoc
*/
componentDidMount() {
this._updateButtonFocus();
}
/**
* React Component method that executes once component is updated.
*
* @param {Object} prevProps - The previous properties, before the update.
* @returns {void}
*/
componentDidUpdate(prevProps) {
// if there is an update in any of the buttons enable/disable props
// update the focus if needed
if (prevProps.okDisabled !== this.props.okDisabled
|| prevProps.cancelDisabled !== this.props.cancelDisabled
|| prevProps.submitDisabled !== this.props.submitDisabled) {
this._updateButtonFocus();
}
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
return (
/**
* Enabled light theme for dialogs until all in-dialog components
* support dark theme.
*/
<AtlasKitThemeProvider mode = 'light'>
<div
onKeyDown = { this._onKeyDown }
ref = { this._setDialogElement }>
<ModalDialog
footer = { this._renderFooter() }
header = { this._renderHeader() }
isOpen = { true }
onDialogDismissed = { this._onDialogDismissed }
width = { this.props.width || 'medium' }>
<div>
<form
className = 'modal-dialog-form'
id = 'modal-dialog-form'
onSubmit = { this._onSubmit }>
{ this.props.children }
</form>
</div>
</ModalDialog>
</div>
</AtlasKitThemeProvider>
);
}
/**
* Dispatches action to hide the dialog.
*
* @returns {void}
*/
_onCancel() {
if (!this.props.isModal) {
this.props.onCancel();
}
}
/**
* Handles click on the blanket area.
*
* @returns {void}
*/
_onDialogDismissed() {
if (!this.props.disableBlanketClickDismiss) {
this._onCancel();
}
}
/**
* Dispatches the action when submitting the dialog.
*
* @private
* @param {string} value - The submitted value if any.
* @returns {void}
*/
_onSubmit(value) {
this.props.onSubmit(value);
}
/**
* Renders Cancel button.
*
* @private
* @returns {*} The Cancel button if enabled and dialog is not modal.
*/
_renderCancelButton() {
if (this.props.cancelDisabled || this.props.isModal) {
return null;
}
return (
<Button
appearance = 'subtle'
id = { CANCEL_BUTTON_ID }
key = 'cancel'
onClick = { this._onCancel }
type = 'button'>
{ this.props.t(this.props.cancelTitleKey || 'dialog.Cancel') }
</Button>
);
}
/**
* Renders component in dialog footer.
*
* @private
* @returns {ReactElement}
*/
_renderFooter() {
// Filter out falsy (null) values because {@code ButtonGroup} will error
// if passed in anything but buttons with valid type props.
const buttons = [
this._renderCancelButton(),
this._renderOKButton()
].filter(Boolean);
return (
<footer className = 'modal-dialog-footer'>
<ButtonGroup>
{ buttons }
</ButtonGroup>
</footer>
);
}
/**
* Renders component in dialog header.
*
* @private
* @returns {ReactElement}
*/
_renderHeader() {
const { t } = this.props;
return (
<header>
<h3>
{ this.props.titleString || t(this.props.titleKey) }
</h3>
</header>
);
}
/**
* Renders OK button.
*
* @private
* @returns {*} The OK button if enabled.
*/
_renderOKButton() {
if (this.props.submitDisabled) {
return null;
}
return (
<Button
appearance = 'primary'
form = 'modal-dialog-form'
id = { OK_BUTTON_ID }
isDisabled = { this.props.okDisabled }
key = 'submit'
onClick = { this._onSubmit }
type = 'button'>
{ this.props.t(this.props.okTitleKey || 'dialog.Ok') }
</Button>
);
}
/**
* Sets the instance variable for the div containing the component's dialog
* element so it can be accessed directly.
*
* @param {Object} element - The DOM element for the component's dialog.
* @private
* @returns {void}
*/
_setDialogElement(element) {
this._dialogElement = element;
}
/**
* Handles 'Enter' key in the dialog to submit/hide dialog depending on
* the available buttons and their disabled state.
*
* @param {Object} event - The key event.
* @private
* @returns {void}
*/
_onKeyDown(event) {
// If the event coming to the dialog has been subject to preventDefault
// we don't handle it here.
if (event.defaultPrevented) {
return;
}
if (event.key === 'Enter') {
event.preventDefault();
event.stopPropagation();
if (this.props.submitDisabled && !this.props.cancelDisabled) {
this._onCancel();
} else if (!this.props.okDisabled) {
this._onSubmit();
}
}
}
/**
* Updates focused button, if we have a reference to the dialog element.
* Focus on available button if there is no focus already.
*
* @private
* @returns {void}
*/
_updateButtonFocus() {
if (this._dialogElement) {
// if we have a focused element inside the dialog, skip changing
// the focus
if (this._dialogElement.contains(document.activeElement)) {
return;
}
let buttonToFocus;
if (this.props.submitDisabled) {
buttonToFocus = this._dialogElement
.querySelector(`[id=${CANCEL_BUTTON_ID}]`);
} else if (!this.props.okDisabled) {
buttonToFocus = this._dialogElement
.querySelector(`[id=${OK_BUTTON_ID}]`);
}
if (buttonToFocus) {
buttonToFocus.focus();
}
}
}
}
export default translate(StatelessDialog);