Add proptypes and a few more refactors
This commit is contained in:
parent
c87f3054b9
commit
4fa10ec656
@ -1,8 +1,9 @@
|
|||||||
import React, { Component, Children, cloneElement } from 'react';
|
import React, { Component, Children, cloneElement } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import styles from './styles';
|
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
|
import UserAction from '/imports/ui/components/user-list/user-list-item/user-action/component';
|
||||||
import KEY_CODES from '/imports/utils/keyCodes';
|
import KEY_CODES from '/imports/utils/keyCodes';
|
||||||
|
import styles from './styles';
|
||||||
import ListItem from './item/component';
|
import ListItem from './item/component';
|
||||||
import ListSeparator from './separator/component';
|
import ListSeparator from './separator/component';
|
||||||
import ListTitle from './title/component';
|
import ListTitle from './title/component';
|
||||||
@ -10,14 +11,16 @@ import ListTitle from './title/component';
|
|||||||
const propTypes = {
|
const propTypes = {
|
||||||
children: PropTypes.arrayOf((propValue, key, componentName, location, propFullName) => {
|
children: PropTypes.arrayOf((propValue, key, componentName, location, propFullName) => {
|
||||||
if (propValue[key].type !== ListItem &&
|
if (propValue[key].type !== ListItem &&
|
||||||
propValue[key].type !== ListSeparator &&
|
propValue[key].type !== ListSeparator &&
|
||||||
propValue[key].type !== ListTitle) {
|
propValue[key].type !== ListTitle &&
|
||||||
|
propValue[key].type !== UserAction) {
|
||||||
return new Error(
|
return new Error(
|
||||||
`Invalid prop \`${propFullName}\` supplied to` +
|
`Invalid prop \`${propFullName}\` supplied to` +
|
||||||
` \`${componentName}\`. Validation failed.`,
|
` \`${componentName}\`. Validation failed.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}),
|
return true;
|
||||||
|
}).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class DropdownList extends Component {
|
export default class DropdownList extends Component {
|
||||||
@ -29,36 +32,31 @@ export default class DropdownList extends Component {
|
|||||||
this.handleItemClick = this.handleItemClick.bind(this);
|
this.handleItemClick = this.handleItemClick.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this._menu.addEventListener('keydown', event=>this.handleItemKeyDown(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.setState({
|
this.setState({
|
||||||
focusedIndex: 0,
|
focusedIndex: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
|
||||||
let { focusedIndex } = this.state;
|
|
||||||
|
|
||||||
this.menuRefs = [];
|
componentDidMount() {
|
||||||
|
this._menu.addEventListener('keydown', event => this.handleItemKeyDown(event));
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < (this._menu.children.length); i++) {
|
componentDidUpdate() {
|
||||||
if (this._menu.children[i].getAttribute("role") === 'menuitem') {
|
const { focusedIndex } = this.state;
|
||||||
this.menuRefs.push(this._menu.children[i]);
|
|
||||||
}
|
const childrens = [].slice.call(this._menu.children);
|
||||||
}
|
this.menuRefs = childrens.filter(child => child.getAttribute('role') === 'menuitem');
|
||||||
|
|
||||||
const activeRef = this.menuRefs[focusedIndex];
|
const activeRef = this.menuRefs[focusedIndex];
|
||||||
|
|
||||||
if (activeRef) {
|
if (activeRef) {
|
||||||
activeRef.focus();
|
activeRef.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleItemKeyDown(event, callback) {
|
handleItemKeyDown(event, callback) {
|
||||||
const { onActionsHide, getDropdownMenuParent, } = this.props;
|
const { getDropdownMenuParent } = this.props;
|
||||||
let nextFocusedIndex = this.state.focusedIndex;
|
let nextFocusedIndex = this.state.focusedIndex;
|
||||||
|
|
||||||
if (KEY_CODES.ARROW_UP === event.which) {
|
if (KEY_CODES.ARROW_UP === event.which) {
|
||||||
@ -68,7 +66,7 @@ export default class DropdownList extends Component {
|
|||||||
|
|
||||||
if (nextFocusedIndex < 0) {
|
if (nextFocusedIndex < 0) {
|
||||||
nextFocusedIndex = this.menuRefs.length - 1;
|
nextFocusedIndex = this.menuRefs.length - 1;
|
||||||
}else if (nextFocusedIndex > this.menuRefs.length - 1) {
|
} else if (nextFocusedIndex > this.menuRefs.length - 1) {
|
||||||
nextFocusedIndex = 0;
|
nextFocusedIndex = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,30 +88,29 @@ export default class DropdownList extends Component {
|
|||||||
|
|
||||||
if ([KEY_CODES.ESCAPE, KEY_CODES.TAB, KEY_CODES.ARROW_LEFT].includes(event.keyCode)) {
|
if ([KEY_CODES.ESCAPE, KEY_CODES.TAB, KEY_CODES.ARROW_LEFT].includes(event.keyCode)) {
|
||||||
const { dropdownHide } = this.props;
|
const { dropdownHide } = this.props;
|
||||||
|
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
dropdownHide();
|
dropdownHide();
|
||||||
if (getDropdownMenuParent) {
|
if (getDropdownMenuParent) {
|
||||||
getDropdownMenuParent().focus();
|
getDropdownMenuParent().focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({focusedIndex: nextFocusedIndex});
|
this.setState({ focusedIndex: nextFocusedIndex });
|
||||||
|
|
||||||
if (typeof callback === 'function') {
|
if (typeof callback === 'function') {
|
||||||
callback(event);
|
callback(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleItemClick(event, callback) {
|
handleItemClick(event, callback) {
|
||||||
const { getDropdownMenuParent, onActionsHide} = this.props;
|
const { getDropdownMenuParent, onActionsHide, dropdownHide } = this.props;
|
||||||
const { dropdownHide } = this.props;
|
|
||||||
|
|
||||||
if ( getDropdownMenuParent ) {
|
if (getDropdownMenuParent) {
|
||||||
onActionsHide();
|
onActionsHide();
|
||||||
}else{
|
} else {
|
||||||
this.setState({ focusedIndex: null });
|
this.setState({ focusedIndex: null });
|
||||||
dropdownHide();
|
dropdownHide();
|
||||||
}
|
}
|
||||||
@ -127,7 +124,7 @@ export default class DropdownList extends Component {
|
|||||||
const { children, style, className } = this.props;
|
const { children, style, className } = this.props;
|
||||||
|
|
||||||
const boundChildren = Children.map(children,
|
const boundChildren = Children.map(children,
|
||||||
(item, i) => {
|
(item) => {
|
||||||
if (item.type === ListSeparator) {
|
if (item.type === ListSeparator) {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
@ -140,13 +137,13 @@ export default class DropdownList extends Component {
|
|||||||
|
|
||||||
onClick: (event) => {
|
onClick: (event) => {
|
||||||
let { onClick } = item.props;
|
let { onClick } = item.props;
|
||||||
onClick = onClick ? onClick.bind(item) : null;
|
onClick = onClick ? () => onClick.call(item) : null;
|
||||||
this.handleItemClick(event, onClick);
|
this.handleItemClick(event, onClick);
|
||||||
},
|
},
|
||||||
|
|
||||||
onKeyDown: (event) => {
|
onKeyDown: (event) => {
|
||||||
let { onKeyDown } = item.props;
|
let { onKeyDown } = item.props;
|
||||||
onKeyDown = onKeyDown ? onKeyDown.bind(item) : null;
|
onKeyDown = onKeyDown ? () => onKeyDown.call(item) : null;
|
||||||
|
|
||||||
this.handleItemKeyDown(event, onKeyDown);
|
this.handleItemKeyDown(event, onKeyDown);
|
||||||
},
|
},
|
||||||
@ -154,11 +151,16 @@ export default class DropdownList extends Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul
|
<ul
|
||||||
style={style}
|
style={style}
|
||||||
className={cx(styles.list, className)}
|
className={cx(styles.list, className)}
|
||||||
role="menu" ref={(r) => this._menu = r}>
|
role="menu"
|
||||||
{boundChildren}
|
ref={(r) => {
|
||||||
|
this._menu = r;
|
||||||
|
return r;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{boundChildren}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ const defaultProps = {
|
|||||||
icon: '',
|
icon: '',
|
||||||
label: '',
|
label: '',
|
||||||
description: '',
|
description: '',
|
||||||
|
tabIndex: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class DropdownListItem extends Component {
|
export default class DropdownListItem extends Component {
|
||||||
|
@ -2,9 +2,9 @@ import React, { Component } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { injectIntl } from 'react-intl';
|
import { injectIntl } from 'react-intl';
|
||||||
import DropdownListItem from '/imports/ui/components/dropdown/list/item/component';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import UserListContent from './user-list-content/component';
|
import UserListContent from './user-list-content/component';
|
||||||
|
import UserAction from './user-action/component';
|
||||||
|
|
||||||
const normalizeEmojiName = (emoji) => {
|
const normalizeEmojiName = (emoji) => {
|
||||||
const emojisNormalized = {
|
const emojisNormalized = {
|
||||||
@ -32,14 +32,33 @@ const propTypes = {
|
|||||||
currentUser: PropTypes.shape({
|
currentUser: PropTypes.shape({
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
|
||||||
|
compact: PropTypes.bool.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
userActions: PropTypes.object.isRequired,
|
||||||
|
router: PropTypes.object.isRequired,
|
||||||
|
isBreakoutRoom: PropTypes.bool.isRequired,
|
||||||
|
getAvailableActions: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
shouldShowActions: false,
|
shouldShowActions: false,
|
||||||
|
isBreakoutRoom: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
class UserListItem extends Component {
|
class UserListItem extends Component {
|
||||||
|
|
||||||
|
static createAction(action, ...options) {
|
||||||
|
return (
|
||||||
|
<UserAction
|
||||||
|
key={_.uniqueId('action-item-')}
|
||||||
|
icon={action.icon}
|
||||||
|
label={action.label}
|
||||||
|
handler={action.handler}
|
||||||
|
options={[...options]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
getUsersActions() {
|
getUsersActions() {
|
||||||
const {
|
const {
|
||||||
currentUser,
|
currentUser,
|
||||||
@ -69,32 +88,16 @@ class UserListItem extends Component {
|
|||||||
allowedToKick,
|
allowedToKick,
|
||||||
allowedToSetPresenter } = actions;
|
allowedToSetPresenter } = actions;
|
||||||
|
|
||||||
|
|
||||||
return _.compact([
|
return _.compact([
|
||||||
(allowedToChatPrivately ? this.renderUserAction(openChat, router, user) : null),
|
(allowedToChatPrivately ? UserListItem.createAction(openChat, router, user) : null),
|
||||||
(allowedToMuteAudio ? this.renderUserAction(unmute, user) : null),
|
(allowedToMuteAudio ? UserListItem.createAction(unmute, user) : null),
|
||||||
(allowedToUnmuteAudio ? this.renderUserAction(mute, user) : null),
|
(allowedToUnmuteAudio ? UserListItem.createAction(mute, user) : null),
|
||||||
(allowedToResetStatus ? this.renderUserAction(clearStatus, user) : null),
|
(allowedToResetStatus ? UserListItem.createAction(clearStatus, user) : null),
|
||||||
(allowedToSetPresenter ? this.renderUserAction(setPresenter, user) : null),
|
(allowedToSetPresenter ? UserListItem.createAction(setPresenter, user) : null),
|
||||||
(allowedToKick ? this.renderUserAction(kick, user) : null),
|
(allowedToKick ? UserListItem.createAction(kick, user) : null),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
renderUserAction(action, ...parameters) {
|
|
||||||
const userAction = (
|
|
||||||
<DropdownListItem
|
|
||||||
key={_.uniqueId('action-item-')}
|
|
||||||
icon={action.icon}
|
|
||||||
label={action.label}
|
|
||||||
defaultMessage={action.label}
|
|
||||||
onClick={action.handler.bind(this, ...parameters)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return userAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
compact,
|
compact,
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import DropdownListItem from '/imports/ui/components/dropdown/list/item/component';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
icon: PropTypes.string.isRequired,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
handler: PropTypes.func.isRequired,
|
||||||
|
options: PropTypes.array.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
const UserActions = (props) => {
|
||||||
|
const { key, icon, label, handler, options } = props;
|
||||||
|
|
||||||
|
const userAction = (
|
||||||
|
<DropdownListItem
|
||||||
|
key={key}
|
||||||
|
icon={icon}
|
||||||
|
label={label}
|
||||||
|
defaultMessage={label}
|
||||||
|
onClick={() => handler.call(this, ...options)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return userAction;
|
||||||
|
};
|
||||||
|
|
||||||
|
UserActions.propTypes = propTypes;
|
||||||
|
export default UserActions;
|
@ -1,6 +1,7 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { defineMessages } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { findDOMNode } from 'react-dom';
|
import { findDOMNode } from 'react-dom';
|
||||||
import UserAvatar from '/imports/ui/components/user-avatar/component';
|
import UserAvatar from '/imports/ui/components/user-avatar/component';
|
||||||
import Icon from '/imports/ui/components/icon/component';
|
import Icon from '/imports/ui/components/icon/component';
|
||||||
@ -41,6 +42,15 @@ const messages = defineMessages({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
compact: PropTypes.bool.isRequired,
|
||||||
|
user: PropTypes.object.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
normalizeEmojiName: PropTypes.func.isRequired,
|
||||||
|
actions: PropTypes.array.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class UserListContent extends Component {
|
class UserListContent extends Component {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -108,7 +118,6 @@ class UserListContent extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the dropdown is visible, if so, check if should be draw on top or bottom direction.
|
* Check if the dropdown is visible, if so, check if should be draw on top or bottom direction.
|
||||||
*/
|
*/
|
||||||
@ -126,7 +135,7 @@ class UserListContent extends Component {
|
|||||||
|
|
||||||
const isDropdownVisible =
|
const isDropdownVisible =
|
||||||
UserListContent.checkIfDropdownIsVisible(dropdownContent.offsetTop,
|
UserListContent.checkIfDropdownIsVisible(dropdownContent.offsetTop,
|
||||||
dropdownContent.offsetHeight);
|
dropdownContent.offsetHeight);
|
||||||
|
|
||||||
if (!isDropdownVisible) {
|
if (!isDropdownVisible) {
|
||||||
const offsetPageTop =
|
const offsetPageTop =
|
||||||
@ -159,19 +168,20 @@ class UserListContent extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
compact,
|
compact,
|
||||||
user,
|
user,
|
||||||
intl,
|
intl,
|
||||||
normalizeEmojiName,
|
normalizeEmojiName,
|
||||||
actions,
|
actions,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isActionsOpen,
|
isActionsOpen,
|
||||||
dropdownVisible,
|
dropdownVisible,
|
||||||
dropdownDirection,
|
dropdownDirection,
|
||||||
dropdownOffset,
|
dropdownOffset,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const userItemContentsStyle = {};
|
const userItemContentsStyle = {};
|
||||||
|
|
||||||
userItemContentsStyle[styles.userItemContentsCompact] = compact;
|
userItemContentsStyle[styles.userItemContentsCompact] = compact;
|
||||||
@ -276,4 +286,5 @@ class UserListContent extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UserListContent.propTypes = propTypes;
|
||||||
export default UserListContent;
|
export default UserListContent;
|
||||||
|
Loading…
Reference in New Issue
Block a user