Dropdown with dropdown_v2 branch

This commit is contained in:
Jaeeun Cho 2016-08-19 17:20:17 -04:00
parent 8a32f61f2f
commit 47f1447bc4
4 changed files with 309 additions and 301 deletions

View File

@ -1,82 +1,86 @@
import React, { Component, PropTypes } from 'react';
import { findDOMNode } from 'react-dom';
import Button from '/imports/ui/components/button/component';
import classNames from 'classnames';
import Icon from '/imports/ui/components/icon/component';
import cx from 'classnames';
import styles from './styles';
import DropdownContent from './DropdownContent';
import DropdownTrigger from './DropdownTrigger';
export default class Dropdown extends Component {
constructor(props) {
super(props);
this.state = { isMenuOpen: false, };
this.showMenu = this.showMenu.bind(this);
this.hideMenu = this.hideMenu.bind(this);
}
showMenu() {
this.setState({ isMenuOpen: !this.state.isMenuOpen, });
}
hideMenu() {
this.setState({ isMenuOpen: false, });
}
componentDidMount () {
const { addEventListener } = window;
addEventListener( 'click', this.onWindowClick.bind(this), false );
}
componentWillUnmount () {
const { removeEventListener } = window;
removeEventListener( 'click', this.onWindowClick.bind(this), false );
}
onWindowClick(event) {
const dropdown_element = findDOMNode(this);
const shouldUpdateState = event.target !== dropdown_element &&
!dropdown_element.contains(event.target) &&
this.state.isMenuOpen;
if(shouldUpdateState) {
this.hideMenu();
}
}
toggle() {
if(this.state.isMenuOpen) {
this.hideMenu();
} else {
this.showMenu();
}
}
render() {
const toggleMenu = this.toggle.bind(this);
// // stick callback on trigger element
const boundChildren = React.Children.map( this.props.children, (child) => {
if( child.type === DropdownTrigger ){
child = React.cloneElement( child, {
toggleMenu: toggleMenu,
});
}
return child;
});
let trigger = boundChildren[0];
let content = boundChildren[1];
return (
<div className={styles.dropdown}>
{trigger}
{ this.state.isMenuOpen ?
content : null}
</div>
);
}
}
import React, { Component, PropTypes } from 'react';
import { findDOMNode } from 'react-dom';
import Button from '/imports/ui/components/button/component';
import Icon from '/imports/ui/components/icon/component';
import classNames from 'classnames';
import styles from './styles';
import DropdownTrigger from './DropdownTrigger';
import DropdownContent from './DropdownContent';
export default class Dropdown extends Component {
constructor(props) {
super(props);
this.state = { isMenuOpen: false, };
this.showMenu = this.showMenu.bind(this);
this.hideMenu = this.hideMenu.bind(this);
}
showMenu(event) {
this.setState({ isMenuOpen: true, });
let pressedKey = event.keyCode;
if (pressedKey === 9 || pressedKey == 40) {
this.props.menuFocus(event);
}
}
hideMenu() {
this.setState({ isMenuOpen: false, });
}
componentDidMount () {
const { addEventListener } = window;
addEventListener('click', this.onWindowClick.bind(this), false);
}
componentWillUnmount () {
const { removeEventListener } = window;
removeEventListener('click', this.onWindowClick.bind(this), false);
}
onWindowClick(event) {
const dropdownElement = findDOMNode(this);
const shouldUpdateState = event.target !== dropdownElement &&
!dropdownElement.contains(event.target) &&
this.state.isMenuOpen;
if (shouldUpdateState) {
this.hideMenu();
}
}
toggle(event) {
if (this.state.isMenuOpen) {
this.hideMenu();
} else {
this.showMenu(event);
}
}
render() {
const toggleMenu = this.toggle.bind(this);
// stick callback on trigger element
const boundChildren = React.Children.map(this.props.children, (child) => {
if (child.type === DropdownTrigger) {
child = React.cloneElement(child, {
toggleMenu: toggleMenu,
});
}
return child;
});
let trigger = boundChildren[0];
let content = boundChildren[1];
return (
<div className={styles.dropdown}>
{trigger}
{ this.state.isMenuOpen ?
content : null }
</div>
);
}
}

View File

@ -1,13 +1,13 @@
import React, { Component, PropTypes } from 'react';
import styles from './styles';
export default class DropdownContent extends Component {
constructor(props) {
super(props);
}
render() {
return <div>{this.props.children}</div>;
}
}
import React, { Component, PropTypes } from 'react';
import styles from './styles';
export default class DropdownContent extends Component {
constructor(props) {
super(props);
}
render() {
return <div>{this.props.children}</div>;
}
}

View File

@ -1,33 +1,26 @@
import React, { Component, PropTypes } from 'react';
import Button from '/imports/ui/components/button/component';
import styles from './styles';
const propTypes = {
labelBtn: PropTypes.string.isRequired,
iconBtn: PropTypes.string.isRequired,
toggleMenu: PropTypes.func.isRequired,
}
export default class DropdownTrigger extends Component {
constructor(props) {
super(props);
}
render() {
const { labelBtn, iconBtn, refBtn } = this.props;
const toggle = this.props.toggleMenu.bind(this);
return (
<Button className={styles.settingBtn}
role='button'
label='setting'
icon='more'
ghost={true}
circle={true}
hideLabel={true}
onClick={toggle}
ref='settingBtn'/>
);
}
}
DropdownTrigger.propTypes = propTypes;
import React, { Component, PropTypes } from 'react';
import Button from '/imports/ui/components/button/component';
import styles from './styles';
export default class DropdownTrigger extends Component {
constructor(props) {
super(props);
}
render() {
const { labelBtn, iconBtn } = this.props;
const toggle = this.props.toggleMenu.bind(this);
return (
<Button className={styles.settingBtn}
role='button'
label={labelBtn}
icon={iconBtn}
ghost={true}
circle={true}
hideLabel={true}
onClick={toggle}
onKeyDown={toggle}
aria-haspopup={'true'}/>
);
}
}

View File

@ -1,173 +1,184 @@
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import Modal from 'react-modal';
import Icon from '/imports/ui/components/icon/component';
import Button from '/imports/ui/components/button/component';
import classNames from 'classnames';
import styles from './styles';
import SettingsModal from '../settings/SettingsModal';
import SessionMenu from '../settings/submenus/SessionMenu';
import Dropdown from './Dropdown';
import DropdownContent from './DropdownContent';
import DropdownTrigger from './DropdownTrigger';
export default class SettingsDropdown extends Component {
constructor(props) {
super(props);
this.menus = [];
}
componentWillMount() {
this.setState({ activeMenu: -1, focusMenu: 0, });
this.menus.push({ className: '',
props: { title: 'Fullscreen', prependIconName: 'icon-',
icon: 'bbb-full-screen', }, tabIndex: 1, });
this.menus.push({ className: SettingsModal,
props: { title: 'Settings/Options', prependIconName: 'icon-',
icon: 'bbb-more', }, tabIndex: 2, });
this.menus.push({ className: SessionMenu,
props: { title: 'Leave Session', prependIconName: 'icon-',
icon: 'bbb-logout', }, tabIndex: 3, });
}
componentWillUpdate() {
if (this.refs.dropdown.state.isMenuOpen && this.state.activeMenu > 0) {
this.setState({ activeMenu: -1, focusMenu: 0, });
}
}
handleListKeyDown(event) {
const pressedKey = event.keyCode;
let menusLength = this.menus.length - 1;
// tab
if (pressedKey === 9) {
let newIndex = 0;
if (this.state.focusMenu >= menusLength) {
newIndex = 0;
} else {
newIndex = this.state.focusMenu + 1;
}
this.setState({ focusMenu: newIndex });
return;
}
// shift + tab
if (event.shiftKey && pressedKey === 9) {
let newIndex = 0;
if (this.state.focusMenu <= 0) {
newIndex = menusLength;
} else {
newIndex = this.state.focusMenu - 1;
}
this.setState({ focusMenu: newIndex });
return;
}
// Up key
if (pressedKey === 38) {
if (this.state.focusMenu <= 0) { // checks if at end of menu
this.setState({ focusMenu: menusLength },
function () { ReactDOM.findDOMNode(this.refs[`menu${this.state.focusMenu}`]).focus();
});
} else {
this.setState({ focusMenu: this.state.focusMenu - 1 },
function () { ReactDOM.findDOMNode(this.refs[`menu${this.state.focusMenu}`]).focus(); });
}
return;
}
// Down key
if (pressedKey === 40) {
if (this.state.focusMenu >= menusLength) { // checks if at end of menu
this.setState({ focusMenu: 0 },
function () { ReactDOM.findDOMNode(this.refs[`menu${this.state.focusMenu}`]).focus();
});
} else {
this.setState({ focusMenu: this.state.focusMenu + 1 },
function () { ReactDOM.findDOMNode(this.refs[`menu${this.state.focusMenu}`]).focus(); });
}
return;
}
// Enter and SpaceBar
if (pressedKey === 13 || pressedKey === 32) {
this.clickMenu(this.state.focusMenu);
return;
}
}
handleFocus(index) {
this.setState({ focusMenu: index });
}
clickMenu(i) {
if (i < 0) {
this.setState({ activeMenu: -1, focusMenu: 0, });
}
if (i >= this.menus.length) {
this.setState({ activeMenu: this.menus.length - 1,
focusMenu: this.menus.length - 1, });
} else {
this.setState({ activeMenu: i, focusMenu: i, });
}
this.refs.dropdown.hideMenu();
}
createMenu() {
const curr = this.state.activeMenu;
if(curr === 0) {
return console.log('full screen trigger');
}
if(curr === 1) {
return <SettingsModal />;
}
if (curr == 2) {
return <SessionMenu />;
}
}
render() {
return (
<div>
<Dropdown ref='dropdown' menuFocus={this.state.focusMenu}>
<DropdownTrigger labelBtn='setting' iconBtn='more' />
<DropdownContent>
<div className={styles.triangleOnDropdown}></div>
<div className={styles.dropdown_active_content}>
<p id="dropdownModal" className={styles.descModal}>Settings dropdown</p>
<ul className={styles.menuList} role="menu">
{this.menus.map((value, index) => (
<li key={index} role='menuitem'
tabIndex={value.tabIndex}
onClick={this.clickMenu.bind(this, index)}
onKeyDown={this.handleListKeyDown.bind(this)}
onFocus={this.handleFocus.bind(this, index)}
ref={'menu' + index}
className={styles.settingsMenuItem}>
<Icon key={index} prependIconName={value.props.prependIconName}
iconName={value.props.icon} title={value.props.title}/>
<span className={styles.settingsMenuItemText}>{value.props.title}</span>
{index == '0' ? <hr /> : null}
</li>
))}
</ul>
</div>
</DropdownContent>
</Dropdown>
<div>{this.createMenu()}</div>
</div>
);
}
}
import React, { Component, PropTyes } from 'react';
import ReactDOM from 'react-dom';
import Icon from '/imports/ui/components/icon/component';
import Button from '/imports/ui/components/button/component';
import classNames from 'classnames';
import styles from './styles';
import SettingsModal from '../settings/SettingsModal';
import SessionMenu from '../settings/submenus/SessionMenu';
import Dropdown from './Dropdown';
import DropdownContent from './DropdownContent';
import DropdownTrigger from './DropdownTrigger';
export default class SettingsDropdown extends Component {
constructor(props) {
super(props);
this.menus = [];
}
componentWillMount() {
this.setState({ activeMenu: -1, focusMenu: 0, });
this.menus.push({ className: '',
props: { title: 'Fullscreen', prependIconName: 'icon-',
icon: 'bbb-full-screen', }, tabIndex: 1, });
this.menus.push({ className: SettingsModal,
props: { title: 'Settings/Options', prependIconName: 'icon-',
icon: 'bbb-more', }, tabIndex: 2, });
this.menus.push({ className: SessionMenu,
props: { title: 'Leave Session', prependIconName: 'icon-',
icon: 'bbb-logout', }, tabIndex: 3, });
}
componentWillUpdate() {
if (!this.refs.dropdown.state.isMenuOpen && this.state.activeMenu > 0) {
this.setState({ activeMenu: -1, focusMenu: 0, });
}
}
// call focus
setFocus() {
ReactDOM.findDOMNode(this.refs[`menu${this.state.focusMenu}`]).focus();
}
handleListKeyDown(event) {
const pressedKey = event.keyCode;
let menusLength = this.menus.length - 1;
// tab
if (pressedKey === 9) {
let newIndex = 0;
if (this.state.focusMenu >= menusLength) {
newIndex = 0;
this.refs.dropdown.hideMenu();
} else {
newIndex = this.state.focusMenu;
}
this.setState({ focusMenu: newIndex });
return;
}
// Down key
if (pressedKey === 40) {
if (this.state.focusMenu >= menusLength) { // checks if at end of menu
this.setState({ focusMenu: 0 },
() => { this.setFocus(); });
} else {
this.setState({ focusMenu: this.state.focusMenu + 1 },
() => { this.setFocus(); });
}
return;
}
// shift + tab
if (event.shiftKey && pressedKey === 9) {
let newIndex = 0;
if (this.state.focusMenu <= 0) {
newIndex = menusLength;
} else {
newIndex = this.state.focusMenu - 1;
}
this.setState({ focusMenu: newIndex });
return;
}
// Up key
if (pressedKey === 38) {
if (this.state.focusMenu <= 0) { // checks if at end of menu
this.setState({ focusMenu: menusLength },
() => { this.setFocus(); });
} else {
this.setState({ focusMenu: this.state.focusMenu - 1 },
() => { this.setFocus(); });
}
return;
}
// Enter and SpaceBar
if (pressedKey === 13 || pressedKey === 32) {
this.clickMenu(this.state.focusMenu);
return;
}
//ESC
if (pressedKey == 27) {
this.setState({ activeMenu: -1, focusMenu: 0 });
this.refs.dropdown.hideMenu();
}
}
handleFocus(index) {
this.setState({ focusMenu: index },
() => { this.setFocus(); });
}
clickMenu(i) {
this.setState({ activeMenu: i });
this.refs.dropdown.hideMenu();
}
createMenu() {
const curr = this.state.activeMenu;
if (curr === 0) {
console.log(this.menus[curr].props.title);
}
if (curr === 1) {
return <SettingsModal />;
}
if (curr === 2) {
return <SessionMenu />;
}
}
openWithKey(event) {
// keep focus is located at the first menu
if (event.keyCode === 9) {
event.preventDefault();
event.stopPropagation();
}
this.setState({ focusMenu: 0 }, () => { this.setFocus(); });
}
render() {
const keyChange = this.openWithKey.bind(this);
return (
<div>
<Dropdown ref='dropdown' menuFocus={keyChange}>
<DropdownTrigger labelBtn='setting' iconBtn='more' />
<DropdownContent ref='content'>
<div className={styles.triangleOnDropdown}></div>
<div className={styles.dropdown_active_content}>
<p id="dropdownModal" className={styles.descModal}>Settings dropdown</p>
<ul className={styles.menuList} role="menu">
{this.menus.map((value, index) => (
<li key={index} role='menuitem'
tabIndex={value.tabIndex}
onClick={this.clickMenu.bind(this, index)}
onKeyDown={this.handleListKeyDown.bind(this)}
onFocus={this.handleFocus.bind(this, index)}
ref={'menu' + index}
className={styles.settingsMenuItem}>
<Icon key={index} prependIconName={value.props.prependIconName}
iconName={value.props.icon} title={value.props.title}/>
<span className={styles.settingsMenuItemText}>{value.props.title}</span>
{index == '0' ? <hr /> : null}
</li>
))}
</ul>
</div>
</DropdownContent>
</Dropdown>
<div>{this.createMenu()}</div>
</div>
);
}
}