feat(ui-components) Add Dialog Component (#12260)
This commit is contained in:
parent
0d917df1fb
commit
bfa88f13dc
|
@ -3,9 +3,9 @@
|
|||
import { AtlasKitThemeProvider } from '@atlaskit/theme';
|
||||
import React from 'react';
|
||||
|
||||
import { DialogContainer } from '../../base/dialog';
|
||||
import GlobalStyles from '../../base/ui/components/GlobalStyles';
|
||||
import JitsiThemeProvider from '../../base/ui/components/JitsiThemeProvider.web';
|
||||
import DialogContainer from '../../base/ui/components/web/DialogContainer';
|
||||
import { ChromeExtensionBanner } from '../../chrome-extension-banner';
|
||||
|
||||
import { AbstractApp } from './AbstractApp';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
import type { Dispatch } from 'redux';
|
||||
import { IStore } from '../../app/types';
|
||||
|
||||
import {
|
||||
HIDE_DIALOG,
|
||||
|
@ -22,7 +22,7 @@ import { isDialogOpen } from './functions';
|
|||
* component: (React.Component | undefined)
|
||||
* }}
|
||||
*/
|
||||
export function hideDialog(component: ?Object) {
|
||||
export function hideDialog(component?: ComponentType) {
|
||||
return {
|
||||
type: HIDE_DIALOG,
|
||||
component
|
||||
|
@ -54,7 +54,7 @@ export function hideSheet() {
|
|||
* componentProps: (Object | undefined)
|
||||
* }}
|
||||
*/
|
||||
export function openDialog(component: Object, componentProps: ?Object) {
|
||||
export function openDialog(component: ComponentType, componentProps?: Object) {
|
||||
return {
|
||||
type: OPEN_DIALOG,
|
||||
component,
|
||||
|
@ -74,7 +74,7 @@ export function openDialog(component: Object, componentProps: ?Object) {
|
|||
* componentProps: (Object | undefined)
|
||||
* }}
|
||||
*/
|
||||
export function openSheet(component: Object, componentProps: ?Object) {
|
||||
export function openSheet(component: ComponentType, componentProps?: Object) {
|
||||
return {
|
||||
type: OPEN_SHEET,
|
||||
component,
|
||||
|
@ -92,8 +92,8 @@ export function openSheet(component: Object, componentProps: ?Object) {
|
|||
* specified {@code component}.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function toggleDialog(component: Object, componentProps: ?Object) {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
export function toggleDialog(component: ComponentType, componentProps?: Object) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
if (isDialogOpen(getState, component)) {
|
||||
dispatch(hideDialog(component));
|
||||
} else {
|
|
@ -1,34 +1,33 @@
|
|||
/* @flow */
|
||||
import React, { Component, ComponentType } from 'react';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { type ReactionEmojiProps } from '../../../reactions/constants';
|
||||
import { IState } from '../../../app/types';
|
||||
import { ReactionEmojiProps } from '../../../reactions/constants';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link DialogContainer}.
|
||||
*/
|
||||
type Props = {
|
||||
interface Props {
|
||||
|
||||
/**
|
||||
* The component to render.
|
||||
*/
|
||||
_component: Function,
|
||||
_component: ComponentType;
|
||||
|
||||
/**
|
||||
* The props to pass to the component that will be rendered.
|
||||
*/
|
||||
_componentProps: Object,
|
||||
|
||||
/**
|
||||
* True if the UI is in a compact state where we don't show dialogs.
|
||||
*/
|
||||
_reducedUI: boolean,
|
||||
_componentProps: Object;
|
||||
|
||||
/**
|
||||
* Array of reactions to be displayed.
|
||||
*/
|
||||
_reactionsQueue: Array<ReactionEmojiProps>
|
||||
};
|
||||
_reactionsQueue: Array<ReactionEmojiProps>;
|
||||
|
||||
/**
|
||||
* True if the UI is in a compact state where we don't show dialogs.
|
||||
*/
|
||||
_reducedUI: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a DialogContainer responsible for showing all dialogs.
|
||||
|
@ -61,7 +60,7 @@ export default class AbstractDialogContainer extends Component<Props> {
|
|||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
export function abstractMapStateToProps(state: Object): $Shape<Props> {
|
||||
export function abstractMapStateToProps(state: IState) {
|
||||
const stateFeaturesBaseDialog = state['features/base/dialog'];
|
||||
const { reducedUI } = state['features/base/responsive-ui'];
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
import { ModalTransition } from '@atlaskit/modal-dialog';
|
||||
import React from 'react';
|
||||
|
||||
import { connect } from '../../../redux';
|
||||
import AbstractDialogContainer, {
|
||||
abstractMapStateToProps
|
||||
} from '../AbstractDialogContainer';
|
||||
|
||||
/**
|
||||
* Implements a DialogContainer responsible for showing all dialogs. Necessary
|
||||
* for supporting @atlaskit's modal animations.
|
||||
*
|
||||
* @augments AbstractDialogContainer
|
||||
*/
|
||||
class DialogContainer extends AbstractDialogContainer {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<ModalTransition>
|
||||
{ this._renderDialogContent() }
|
||||
</ModalTransition>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(abstractMapStateToProps)(DialogContainer);
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
export { default as AbstractDialogTab } from './AbstractDialogTab';
|
||||
export type { Props as AbstractDialogTabProps } from './AbstractDialogTab';
|
||||
export { default as Dialog } from './Dialog';
|
||||
export { default as DialogContainer } from './DialogContainer';
|
||||
export { default as Dialog } from './Dialog-old';
|
||||
export { default as DialogWithTabs } from './DialogWithTabs';
|
||||
export { default as StatelessDialog } from './StatelessDialog';
|
||||
export { default as DialogContainer } from '../../../ui/components/web/DialogContainer';
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
/* @flow */
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
import { IState } from '../../app/types';
|
||||
import { IStateful } from '../app/types';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { ColorSchemeRegistry } from '../color-scheme';
|
||||
import { toState } from '../redux';
|
||||
import { toState } from '../redux/functions';
|
||||
|
||||
/**
|
||||
* Checks if any {@code Dialog} is currently open.
|
||||
*
|
||||
* @param {Function|Object} stateful - The redux store, the redux
|
||||
* @param {IStateful} stateful - The redux store, the redux
|
||||
* {@code getState} function, or the redux state itself.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isAnyDialogOpen(stateful: Function) {
|
||||
export function isAnyDialogOpen(stateful: IStateful) {
|
||||
return Boolean(toState(stateful)['features/base/dialog'].component);
|
||||
}
|
||||
|
||||
|
@ -18,25 +22,25 @@ export function isAnyDialogOpen(stateful: Function) {
|
|||
* Checks if a {@code Dialog} with a specific {@code component} is currently
|
||||
* open.
|
||||
*
|
||||
* @param {Function|Object} stateful - The redux store, the redux
|
||||
* @param {IStateful} stateful - The redux store, the redux
|
||||
* {@code getState} function, or the redux state itself.
|
||||
* @param {React.Component} component - The {@code component} of a
|
||||
* {@code Dialog} to be checked.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isDialogOpen(stateful: Function | Object, component: Object) {
|
||||
export function isDialogOpen(stateful: IStateful, component: ComponentType) {
|
||||
return toState(stateful)['features/base/dialog'].component === component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of the Redux state to the props of any Dialog based component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {IState} state - The Redux state.
|
||||
* @returns {{
|
||||
* _dialogStyles: StyleType
|
||||
* }}
|
||||
*/
|
||||
export function _abstractMapStateToProps(state: Object): Object {
|
||||
export function _abstractMapStateToProps(state: IState) {
|
||||
return {
|
||||
_dialogStyles: ColorSchemeRegistry.get(state, 'Dialog')
|
||||
};
|
|
@ -24,6 +24,11 @@ const useStyles = makeStyles()((theme: Theme) => {
|
|||
backgroundColor: theme.palette.ui02
|
||||
},
|
||||
|
||||
'&:focus': {
|
||||
outline: 0,
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`
|
||||
},
|
||||
|
||||
'&:active': {
|
||||
backgroundColor: theme.palette.ui03
|
||||
},
|
||||
|
|
|
@ -0,0 +1,250 @@
|
|||
import { Theme } from '@mui/material';
|
||||
import React, { ReactElement, useCallback, useContext, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { keyframes } from 'tss-react';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { IconClose } from '../../../icons/svg';
|
||||
import { withPixelLineHeight } from '../../../styles/functions.web';
|
||||
|
||||
import Button from './Button';
|
||||
import ClickableIcon from './ClickableIcon';
|
||||
import { DialogTransitionContext } from './DialogTransition';
|
||||
|
||||
|
||||
const useStyles = makeStyles()((theme: Theme) => {
|
||||
return {
|
||||
container: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'flex-start',
|
||||
animation: `${keyframes`
|
||||
0% {
|
||||
opacity: 0.4;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
`} 0.2s forwards ease-out`,
|
||||
|
||||
'&.unmount': {
|
||||
animation: `${keyframes`
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
`} 0.15s forwards ease-in`
|
||||
}
|
||||
},
|
||||
|
||||
backdrop: {
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
top: 0,
|
||||
left: 0,
|
||||
backgroundColor: theme.palette.ui02,
|
||||
opacity: 0.75
|
||||
},
|
||||
|
||||
modal: {
|
||||
zIndex: 1,
|
||||
backgroundColor: theme.palette.ui01,
|
||||
border: `1px solid ${theme.palette.ui03}`,
|
||||
boxShadow: '0px 4px 25px 4px rgba(20, 20, 20, 0.6)',
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: 'auto',
|
||||
minHeight: '200px',
|
||||
maxHeight: '560px',
|
||||
marginTop: '64px',
|
||||
animation: `${keyframes`
|
||||
0% {
|
||||
margin-top: 85px
|
||||
}
|
||||
100% {
|
||||
margin-top: 64px
|
||||
}
|
||||
`} 0.2s forwards ease-out`,
|
||||
|
||||
'&.medium': {
|
||||
width: '400px'
|
||||
},
|
||||
|
||||
'&.large': {
|
||||
width: '664px'
|
||||
},
|
||||
|
||||
'&.unmount': {
|
||||
animation: `${keyframes`
|
||||
0% {
|
||||
margin-top: 64px
|
||||
}
|
||||
100% {
|
||||
margin-top: 40px
|
||||
}
|
||||
`} 0.15s forwards ease-in`
|
||||
},
|
||||
|
||||
'@media (max-width: 448px)': {
|
||||
width: '100% !important',
|
||||
maxHeight: 'initial',
|
||||
height: '100%',
|
||||
margin: 0,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
animation: `${keyframes`
|
||||
0% {
|
||||
margin-top: 15px
|
||||
}
|
||||
100% {
|
||||
margin-top: 0
|
||||
}
|
||||
`} 0.2s forwards ease-out`,
|
||||
|
||||
'&.unmount': {
|
||||
animation: `${keyframes`
|
||||
0% {
|
||||
margin-top: 0
|
||||
}
|
||||
100% {
|
||||
margin-top: 15px
|
||||
}
|
||||
`} 0.15s forwards ease-in`
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
header: {
|
||||
width: '100%',
|
||||
padding: '24px',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'space-between'
|
||||
},
|
||||
|
||||
title: {
|
||||
color: theme.palette.text01,
|
||||
...withPixelLineHeight(theme.typography.heading5),
|
||||
margin: 0,
|
||||
padding: 0
|
||||
},
|
||||
|
||||
content: {
|
||||
height: 'auto',
|
||||
overflowY: 'auto',
|
||||
width: '100%',
|
||||
boxSizing: 'border-box',
|
||||
padding: '0 24px',
|
||||
overflowX: 'hidden',
|
||||
|
||||
'@media (max-width: 448px)': {
|
||||
height: '100%'
|
||||
}
|
||||
},
|
||||
|
||||
footer: {
|
||||
width: '100%',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
padding: '24px',
|
||||
|
||||
'& button:last-child': {
|
||||
marginLeft: '16px'
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
interface DialogProps {
|
||||
cancelKey?: string;
|
||||
children?: ReactElement | ReactElement[];
|
||||
description?: string;
|
||||
ok?: {
|
||||
disabled?: boolean;
|
||||
key: string;
|
||||
};
|
||||
onCancel: () => void;
|
||||
onSubmit?: () => void;
|
||||
size?: 'large' | 'medium';
|
||||
title?: string;
|
||||
titleKey?: string;
|
||||
}
|
||||
|
||||
const Dialog = ({
|
||||
title,
|
||||
titleKey,
|
||||
description,
|
||||
size = 'medium',
|
||||
onCancel,
|
||||
children,
|
||||
ok,
|
||||
cancelKey,
|
||||
onSubmit
|
||||
}: DialogProps) => {
|
||||
const { classes, cx } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
const { isUnmounting } = useContext(DialogTransitionContext);
|
||||
|
||||
const handleKeyDown = useCallback((e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
onCancel();
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className = { cx(classes.container, isUnmounting && 'unmount') }>
|
||||
<div
|
||||
className = { classes.backdrop }
|
||||
onClick = { onCancel } />
|
||||
<div
|
||||
aria-describedby = { description }
|
||||
aria-labelledby = { title ?? t(titleKey ?? '') }
|
||||
aria-modal = { true }
|
||||
className = { cx(classes.modal, isUnmounting && 'unmount', size) }
|
||||
role = 'dialog'>
|
||||
<div className = { classes.header }>
|
||||
<p className = { classes.title }>{title ?? t(titleKey ?? '')}</p>
|
||||
<ClickableIcon
|
||||
accessibilityLabel = { t('dialog.close') }
|
||||
icon = { IconClose }
|
||||
onClick = { onCancel } />
|
||||
</div>
|
||||
<div className = { classes.content }>{children}</div>
|
||||
<div className = { classes.footer }>
|
||||
{cancelKey && <Button
|
||||
accessibilityLabel = { t(cancelKey) }
|
||||
labelKey = { cancelKey }
|
||||
onClick = { onCancel }
|
||||
type = 'tertiary' />}
|
||||
{ok && <Button
|
||||
accessibilityLabel = { t(ok.key) }
|
||||
disabled = { ok.disabled }
|
||||
labelKey = { ok.key }
|
||||
onClick = { onSubmit } />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dialog;
|
|
@ -0,0 +1,138 @@
|
|||
import { ModalTransition } from '@atlaskit/modal-dialog';
|
||||
import React, { Component, ComponentType } from 'react';
|
||||
|
||||
import { IState } from '../../../../app/types';
|
||||
import KeyboardShortcutsDialog from '../../../../keyboard-shortcuts/components/web/KeyboardShortcutsDialog';
|
||||
import { ReactionEmojiProps } from '../../../../reactions/constants';
|
||||
import { connect } from '../../../redux/functions';
|
||||
|
||||
import DialogTransition from './DialogTransition';
|
||||
|
||||
interface Props {
|
||||
|
||||
/**
|
||||
* The component to render.
|
||||
*/
|
||||
_component: ComponentType;
|
||||
|
||||
/**
|
||||
* The props to pass to the component that will be rendered.
|
||||
*/
|
||||
_componentProps: Object;
|
||||
|
||||
/**
|
||||
* Array of reactions to be displayed.
|
||||
*/
|
||||
_reactionsQueue: Array<ReactionEmojiProps>;
|
||||
|
||||
/**
|
||||
* True if the UI is in a compact state where we don't show dialogs.
|
||||
*/
|
||||
_reducedUI: boolean;
|
||||
}
|
||||
|
||||
// This function is necessary while the transition from @atlaskit dialog to our component is ongoing.
|
||||
const isNewDialog = (component: any) => {
|
||||
const list = [ KeyboardShortcutsDialog ];
|
||||
|
||||
return Boolean(list.find(comp => comp === component));
|
||||
};
|
||||
|
||||
// Needed for the transition to our component.
|
||||
type State = {
|
||||
isNewDialog: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a DialogContainer responsible for showing all dialogs. Necessary
|
||||
* for supporting @atlaskit's modal animations.
|
||||
*
|
||||
*/
|
||||
class DialogContainer extends Component<Props, State> {
|
||||
|
||||
/**
|
||||
* Initializes a new {@code DialogContainer} instance.
|
||||
*
|
||||
* @param {Props} props - The React {@code Component} props to initialize
|
||||
* the new {@code DialogContainer} instance with.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isNewDialog: false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check which Dialog container to render.
|
||||
* Needed during transition from atlaskit.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {void}
|
||||
*/
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
if (this.props._component && prevProps._component !== this.props._component) {
|
||||
// eslint-disable-next-line react/no-did-update-set-state
|
||||
this.setState({
|
||||
isNewDialog: isNewDialog(this.props._component)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dialog to be displayed.
|
||||
*
|
||||
* @private
|
||||
* @returns {ReactElement|null}
|
||||
*/
|
||||
_renderDialogContent() {
|
||||
const {
|
||||
_component: component,
|
||||
_reducedUI: reducedUI
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
component && !reducedUI
|
||||
? React.createElement(component, this.props._componentProps)
|
||||
: null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return this.state.isNewDialog ? (
|
||||
<DialogTransition>
|
||||
{this._renderDialogContent()}
|
||||
</DialogTransition>
|
||||
) : (
|
||||
<ModalTransition>
|
||||
{ this._renderDialogContent() }
|
||||
</ModalTransition>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the associated
|
||||
* {@code AbstractDialogContainer}'s props.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
function mapStateToProps(state: IState) {
|
||||
const stateFeaturesBaseDialog = state['features/base/dialog'];
|
||||
const { reducedUI } = state['features/base/responsive-ui'];
|
||||
|
||||
return {
|
||||
_component: stateFeaturesBaseDialog.component,
|
||||
_componentProps: stateFeaturesBaseDialog.componentProps,
|
||||
_reducedUI: reducedUI
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(DialogContainer);
|
|
@ -0,0 +1,28 @@
|
|||
import React, { ReactElement, useEffect, useState } from 'react';
|
||||
|
||||
export const DialogTransitionContext = React.createContext({ isUnmounting: false });
|
||||
|
||||
const DialogTransition = ({ children }: { children: ReactElement | null; }) => {
|
||||
const [ childrenToRender, setChildrenToRender ] = useState(children);
|
||||
const [ isUnmounting, setIsUnmounting ] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (children === null) {
|
||||
setIsUnmounting(true);
|
||||
setTimeout(() => {
|
||||
setChildrenToRender(children);
|
||||
setIsUnmounting(false);
|
||||
}, 150);
|
||||
} else {
|
||||
setChildrenToRender(children);
|
||||
}
|
||||
}, [ children ]);
|
||||
|
||||
return (
|
||||
<DialogTransitionContext.Provider value = {{ isUnmounting }}>
|
||||
{childrenToRender}
|
||||
</DialogTransitionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default DialogTransition;
|
|
@ -3,10 +3,12 @@ import { withStyles } from '@mui/styles';
|
|||
import clsx from 'clsx';
|
||||
import React, { Component } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
// @ts-ignore
|
||||
import { Dialog } from '../../../base/dialog';
|
||||
import { IStore } from '../../../app/types';
|
||||
import { hideDialog } from '../../../base/dialog/actions';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import Dialog from '../../../base/ui/components/web/Dialog';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of
|
||||
|
@ -14,6 +16,11 @@ import { translate } from '../../../base/i18n/functions';
|
|||
*/
|
||||
interface Props extends WithTranslation {
|
||||
|
||||
/**
|
||||
* Dispatches close dialog.
|
||||
*/
|
||||
_onCloseDialog: () => void;
|
||||
|
||||
/**
|
||||
* An object containing the CSS classes.
|
||||
*/
|
||||
|
@ -73,10 +80,8 @@ class KeyboardShortcutsDialog extends Component<Props> {
|
|||
|
||||
return (
|
||||
<Dialog
|
||||
cancelKey = { 'dialog.close' }
|
||||
submitDisabled = { true }
|
||||
titleKey = 'keyboardShortcuts.keyboardShortcuts'
|
||||
width = 'small'>
|
||||
onCancel = { this.props._onCloseDialog }
|
||||
titleKey = 'keyboardShortcuts.keyboardShortcuts'>
|
||||
<div
|
||||
id = 'keyboard-shortcuts'>
|
||||
<ul
|
||||
|
@ -125,4 +130,16 @@ class KeyboardShortcutsDialog extends Component<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
export default translate(withStyles(styles)(KeyboardShortcutsDialog));
|
||||
/**
|
||||
* Function that maps parts of Redux actions into component props.
|
||||
*
|
||||
* @param {Object} dispatch - Redux dispatch.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function mapDispatchToProps(dispatch: IStore['dispatch']) {
|
||||
return {
|
||||
_onCloseDialog: () => dispatch(hideDialog())
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(translate(withStyles(styles)(KeyboardShortcutsDialog)));
|
||||
|
|
Loading…
Reference in New Issue