diff --git a/docs/app/Components/Sidebar/Sidebar.js b/docs/app/Components/Sidebar/Sidebar.js index 52a2f3adc2..722aa81975 100644 --- a/docs/app/Components/Sidebar/Sidebar.js +++ b/docs/app/Components/Sidebar/Sidebar.js @@ -1,7 +1,7 @@ import _ from 'lodash/fp' import React, { Component, PropTypes } from 'react' import { findDOMNode } from 'react-dom' -import { Link } from 'react-router' +import { Link, routerShape } from 'react-router' import pkg from 'package.json' import * as stardust from 'src' @@ -14,13 +14,40 @@ import { Input, } from 'src' +const parentComponents = _.flow( + _.filter(META.isParent), + _.sortBy('_meta.name') +)(stardust) + +const getRoute = (_meta) => `/${_meta.type}s/${_.kebabCase(_meta.name)}` + +const MenuItem = ({ meta, children, ...rest }) => ( + + {children || meta.name} + +) +MenuItem.propTypes = { + activeClassName: PropTypes.string, + children: PropTypes.node, + className: PropTypes.string, + meta: PropTypes.object.isRequired, + onClick: PropTypes.func.isRequired, +} +MenuItem.defaultProps = { + activeClassName: 'active', + className: 'item', +} +const selectedItemLabel = Press Enter + export default class Sidebar extends Component { + static contextTypes = { + router: routerShape, + } static propTypes = { style: PropTypes.object, } state = { query: '' } - - handleSearchChange = e => this.setState({ query: e.target.value }) + filteredComponents = parentComponents componentDidMount() { document.addEventListener('keydown', this.handleDocumentKeyDown) @@ -39,35 +66,106 @@ export default class Sidebar extends Component { if (!hasModifier && isAZ && bodyHasFocus) this._searchInput.focus() } - renderItemsByType = (type) => { + handleItemClick = () => { + const { query } = this.state + + if (query) this.setState({ query: '' }) + if (document.activeElement === this._searchInput) this._searchInput.blur() + } + + handleSearchChange = e => this.setState({ + selectedItemIndex: 0, + query: e.target.value, + }) + + handleSearchKeyDown = e => { + const { router } = this.context + const { selectedItemIndex } = this.state + const code = keyboardKey.getCode(e) + + if (code === keyboardKey.Enter && this.selectedRoute) { + e.preventDefault() + router.push(this.selectedRoute) + this.selectedRoute = null + this._searchInput.blur() + this.setState({ query: '' }) + } + + if (code === keyboardKey.ArrowDown) { + e.preventDefault() + const next = _.min([selectedItemIndex + 1, this.filteredComponents.length - 1]) + this.selectedRoute = getRoute(this.filteredComponents[next]._meta) + this.setState({ selectedItemIndex: next }) + } + + if (code === keyboardKey.ArrowUp) { + e.preventDefault() + const next = _.max([selectedItemIndex - 1, 0]) + this.selectedRoute = getRoute(this.filteredComponents[next]._meta) + this.setState({ selectedItemIndex: next }) + } + } + + menuItemsByType = _.map((type) => { const items = _.flow( - _.filter(_.overEvery([ - META.isParent, - META.isType(type), - ({ _meta }) => new RegExp(this.state.query, 'i').test(_meta.name), - ])), - _.sortBy('_meta.name'), - _.map(({ _meta }) => { - const route = `/${_meta.type}s/${_.kebabCase(_meta.name)}` - - return ( - - {_meta.name} - - ) - }) - )(stardust) - - return _.isEmpty(items) ? [] : ( + _.filter(META.isType(type)), + _.map(({ _meta }) => ( + + )) + )(parentComponents) + + return (