appirio-tech-react-components-test10
Version:
Topcoder UI Kit library of components for our React apps.
188 lines (177 loc) • 5.67 kB
JSX
require('./Dropdown.scss')
import React from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import enhanceDropdown from './enhanceDropdown'
class Dropdown extends React.Component {
constructor(props) {
super(props)
}
render() {
const props = this.props
const { children, className, pointerShadow, noPointer, pointerLeft, isOpen, handleClick, theme, noAutoclose, handleKeyboardNavigation } = props
const ddClasses = classNames('dropdown-wrap', {
[`${className}`] : true,
[`${ theme }`] : true
})
const ndClasses = classNames('Dropdown', {
'pointer-shadow' : pointerShadow,
'pointer-hide' : noPointer,
'pointer-left' : pointerLeft,
'no-autoclose' : noAutoclose,
hide : !isOpen
})
let childSelectionIndex = -1
const focusOnNextChild = () => {
const listChild = this.listRef.getElementsByTagName('li')
if (listChild.length === 0) {
return
}
childSelectionIndex += 1
if (childSelectionIndex >= listChild.length) {
childSelectionIndex -= 1
} else {
listChild[childSelectionIndex].focus()
}
}
const focusOnPreviousChild = () => {
const listChild = this.listRef.getElementsByTagName('li')
if (listChild.length === 0) {
return
}
childSelectionIndex -= 1
if (childSelectionIndex < 0) {
childSelectionIndex = 0
} else {
listChild[childSelectionIndex].focus()
}
}
let searchKey = ''
let timer
const focusOnCharacter = (value) => {
searchKey += value
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => { searchKey = '' }, 500)
const listChild = this.listRef.getElementsByTagName('li')
if (listChild.length === 0) {
return
}
const length = listChild.length
for (let i = 0; i < length; i++) {
let textContent = listChild[i].textContent
if (textContent && textContent.length > 0) {
textContent = textContent.toLowerCase()
const search = searchKey.toLowerCase()
if (textContent.startsWith(search)) {
childSelectionIndex = i
listChild[i].focus()
return true
}
}
}
return false
}
const onFocus = () => {
this.containerRef.classList.add('focused')
}
const onBlur = () => {
this.containerRef.classList.remove('focused')
}
const onKeydown = (e) => {
if (!handleKeyboardNavigation) {
return
}
const keyCode = e.keyCode
if (keyCode === 32 || keyCode === 38 || keyCode === 40) { // space or Up/Down
// open dropdown menu
if (!noAutoclose && !isOpen) {
e.preventDefault()
handleClick(event)
} else {
if (keyCode === 40) {
focusOnNextChild()
} else if (keyCode === 38) {
focusOnPreviousChild()
}
e.preventDefault()
}
} else if (isOpen) {
const value = String.fromCharCode(e.keyCode)
if (focusOnCharacter(value)) {
e.preventDefault()
}
}
}
const onChildKeydown = (e) => {
if (!handleKeyboardNavigation) {
return
}
const keyCode = e.keyCode
if (keyCode === 38 || keyCode === 40 || keyCode === 13) { // Up/Down or enter
if (keyCode === 40) {
focusOnNextChild()
} else if (keyCode === 38) {
focusOnPreviousChild()
} else if (keyCode === 13) { // enter
const listChild = this.listRef.getElementsByTagName('li')
if (listChild.length === 0) {
return
}
listChild[childSelectionIndex].click()
this.handleKeyboardRef.focus()
}
e.preventDefault()
} else {
const value = String.fromCharCode(e.keyCode)
if (focusOnCharacter(value)) {
e.preventDefault()
}
}
}
const setListRef = (c) => this.listRef = c
const setContainerRef = (c) => this.containerRef = c
const setHandleKeyboardRef = (c) => this.handleKeyboardRef = c
const childrenWithProps = React.Children.map(children, child =>
React.cloneElement(child, {onKeyDown: onChildKeydown})
)
return (
<div ref={setContainerRef} className={ddClasses} onClick={noAutoclose ? () => { } : handleClick}>
{handleKeyboardNavigation && (<a ref={setHandleKeyboardRef} tabIndex="0" onFocus={onFocus} onBlur={onBlur} onKeyDown={onKeydown} className="handle-keyboard" href="javascript:;"></a>)}
{
childrenWithProps.map((child, index) => {
if (child.props.className.indexOf('dropdown-menu-header') > -1)
return noAutoclose ? React.cloneElement(child, {
onClick: handleClick,
key: child.props.key || index
}) : child
})
}
<div ref={setListRef} className = {ndClasses}>
{
childrenWithProps.map((child) => {
if (child.props.className.indexOf('dropdown-menu-list') > -1)
return child
})
}
</div>
</div>
)
}
}
Dropdown.propTypes = {
children: PropTypes.array.isRequired,
/*
If true, prevents dropdown closing when clicked inside dropdown
*/
noAutoclose: PropTypes.bool,
/*
If true, prevents handle keyboard event
*/
handleKeyboardNavigation: PropTypes.bool
}
Dropdown.defaultProps = {
handleKeyboardNavigation: false
}
export default enhanceDropdown(Dropdown)