Implemented key bindings in Menu

This commit is contained in:
jos 2017-07-19 20:50:05 +02:00
parent 3ca23568cf
commit 633daf6c95
3 changed files with 74 additions and 16 deletions

View File

@ -58,11 +58,8 @@ const KEY_BINDINGS = {
'left': ['Alt+Left', 'Option+Left'],
'right': ['Alt+Right', 'Option+Right'],
'openUrl': ['Ctrl+Enter', 'Command+Enter']
// TODO: implement all quick keys
// Ctrl+Shift+Arrow Up/Down Select multiple fields
// Shift+Alt+Arrows Move current field or selected fields up/down/left/right
// Ctrl+Z Undo last action
// Ctrl+Shift+Z Redo
// TODO: implement Ctrl+Shift+Arrow Up/Down Select multiple fields
// TODO: implement Shift+Alt+Arrows Move current field or selected fields up/down/left/right
}
export default class TreeMode extends Component {

View File

@ -1,8 +1,12 @@
import { createElement as h, Component } from 'react'
import { findParentWithAttribute } from '../../utils/domUtils'
import { keyComboFromEvent } from '../../utils/keyBindings'
import { findParentWithClassName } from '../../utils/domUtils'
export let CONTEXT_MENU_HEIGHT = 240
const MENU_CLASS_NAME = 'jsoneditor-actionmenu'
const MENU_ITEM_CLASS_NAME = 'jsoneditor-menu-item'
export default class Menu extends Component {
/**
@ -33,12 +37,13 @@ export default class Menu extends Component {
// TODO: create a non-visible button to set the focus to the menu
const className = 'jsoneditor-actionmenu ' +
const className = MENU_CLASS_NAME + ' ' +
((orientation === 'top') ? 'jsoneditor-actionmenu-top' : 'jsoneditor-actionmenu-bottom')
return h('div', {
className: className,
'data-menu': 'true',
ref: 'menu',
onKeyDown: this.handleKeyDown
},
this.props.items.map(this.renderMenuItem)
)
@ -151,11 +156,17 @@ export default class Menu extends Component {
componentDidMount () {
this.updateRequestCloseListener()
if (this.props.open) {
this.focusToFirstEntry ()
}
}
componentDidUpdate () {
componentDidUpdate (prevProps, prevState) {
this.updateRequestCloseListener()
if (this.props.open && !prevProps.open) {
this.focusToFirstEntry ()
}
}
componentWillUnmount () {
@ -163,6 +174,15 @@ export default class Menu extends Component {
setTimeout(() => this.removeRequestCloseListener())
}
focusToFirstEntry () {
if (this.refs.menu) {
const firstButton = this.refs.menu.querySelector('button')
if (firstButton) {
firstButton.focus()
}
}
}
updateRequestCloseListener () {
if (this.props.open) {
this.addRequestCloseListener()
@ -178,10 +198,8 @@ export default class Menu extends Component {
setTimeout(() => {
if (!this.handleRequestClose) {
this.handleRequestClose = (event) => {
if (!findParentWithAttribute(event.target, 'data-menu', 'true')) {
this.props.onRequestClose()
}
}
window.addEventListener('click', this.handleRequestClose)
}
})
@ -194,5 +212,51 @@ export default class Menu extends Component {
}
}
// TODO: implement the same in ModeMenu
handleKeyDown = (event) => {
const combo = keyComboFromEvent (event)
if (combo === 'Up') {
event.preventDefault()
const items = Menu.getItems (event.target)
const index = items.findIndex(item => item === event.target.parentNode)
const prev = items[index - 1]
if (prev) {
prev.querySelector('button').focus()
}
}
if (combo === 'Down') {
event.preventDefault()
const items = Menu.getItems (event.target)
const index = items.findIndex(item => item === event.target.parentNode)
const next = items[index + 1]
if (next) {
next.querySelector('button').focus()
}
}
if (combo === 'Left') {
const left = event.target.previousSibling
if (left && left.nodeName === 'BUTTON') {
left.focus()
}
}
if (combo === 'Right') {
const right = event.target.nextSibling
if (right && right.nodeName === 'BUTTON') {
right.focus()
}
}
}
static getItems (element) {
const menu = findParentWithClassName(element, MENU_CLASS_NAME)
return Array.from(menu.querySelectorAll('.' + MENU_ITEM_CLASS_NAME))
.filter(item => window.getComputedStyle(item).visibility === 'visible')
}
handleRequestClose = null
}

View File

@ -1,6 +1,5 @@
import { createElement as h, Component } from 'react'
import { toCapital } from '../../utils/stringUtils'
import { findParentWithAttribute } from '../../utils/domUtils'
export default class ModeMenu extends Component {
/**
@ -63,10 +62,8 @@ export default class ModeMenu extends Component {
setTimeout(() => {
if (!this.handleRequestClose) {
this.handleRequestClose = (event) => {
if (!findParentWithAttribute(event.target, 'data-menu', 'true')) {
this.props.onRequestClose()
}
}
window.addEventListener('click', this.handleRequestClose)
}
})