Implemented key bindings in Menu
This commit is contained in:
parent
3ca23568cf
commit
633daf6c95
|
@ -58,11 +58,8 @@ const KEY_BINDINGS = {
|
||||||
'left': ['Alt+Left', 'Option+Left'],
|
'left': ['Alt+Left', 'Option+Left'],
|
||||||
'right': ['Alt+Right', 'Option+Right'],
|
'right': ['Alt+Right', 'Option+Right'],
|
||||||
'openUrl': ['Ctrl+Enter', 'Command+Enter']
|
'openUrl': ['Ctrl+Enter', 'Command+Enter']
|
||||||
// TODO: implement all quick keys
|
// TODO: implement Ctrl+Shift+Arrow Up/Down Select multiple fields
|
||||||
// Ctrl+Shift+Arrow Up/Down Select multiple fields
|
// TODO: implement Shift+Alt+Arrows Move current field or selected fields up/down/left/right
|
||||||
// Shift+Alt+Arrows Move current field or selected fields up/down/left/right
|
|
||||||
// Ctrl+Z Undo last action
|
|
||||||
// Ctrl+Shift+Z Redo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class TreeMode extends Component {
|
export default class TreeMode extends Component {
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
import { createElement as h, Component } from 'react'
|
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
|
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 {
|
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
|
// 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')
|
((orientation === 'top') ? 'jsoneditor-actionmenu-top' : 'jsoneditor-actionmenu-bottom')
|
||||||
|
|
||||||
return h('div', {
|
return h('div', {
|
||||||
className: className,
|
className: className,
|
||||||
'data-menu': 'true',
|
ref: 'menu',
|
||||||
|
onKeyDown: this.handleKeyDown
|
||||||
},
|
},
|
||||||
this.props.items.map(this.renderMenuItem)
|
this.props.items.map(this.renderMenuItem)
|
||||||
)
|
)
|
||||||
|
@ -151,11 +156,17 @@ export default class Menu extends Component {
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
this.updateRequestCloseListener()
|
this.updateRequestCloseListener()
|
||||||
|
|
||||||
|
if (this.props.open) {
|
||||||
|
this.focusToFirstEntry ()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate () {
|
componentDidUpdate (prevProps, prevState) {
|
||||||
this.updateRequestCloseListener()
|
this.updateRequestCloseListener()
|
||||||
|
|
||||||
|
if (this.props.open && !prevProps.open) {
|
||||||
|
this.focusToFirstEntry ()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
|
@ -163,6 +174,15 @@ export default class Menu extends Component {
|
||||||
setTimeout(() => this.removeRequestCloseListener())
|
setTimeout(() => this.removeRequestCloseListener())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
focusToFirstEntry () {
|
||||||
|
if (this.refs.menu) {
|
||||||
|
const firstButton = this.refs.menu.querySelector('button')
|
||||||
|
if (firstButton) {
|
||||||
|
firstButton.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateRequestCloseListener () {
|
updateRequestCloseListener () {
|
||||||
if (this.props.open) {
|
if (this.props.open) {
|
||||||
this.addRequestCloseListener()
|
this.addRequestCloseListener()
|
||||||
|
@ -178,9 +198,7 @@ export default class Menu extends Component {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!this.handleRequestClose) {
|
if (!this.handleRequestClose) {
|
||||||
this.handleRequestClose = (event) => {
|
this.handleRequestClose = (event) => {
|
||||||
if (!findParentWithAttribute(event.target, 'data-menu', 'true')) {
|
this.props.onRequestClose()
|
||||||
this.props.onRequestClose()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
window.addEventListener('click', this.handleRequestClose)
|
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
|
handleRequestClose = null
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { createElement as h, Component } from 'react'
|
import { createElement as h, Component } from 'react'
|
||||||
import { toCapital } from '../../utils/stringUtils'
|
import { toCapital } from '../../utils/stringUtils'
|
||||||
import { findParentWithAttribute } from '../../utils/domUtils'
|
|
||||||
|
|
||||||
export default class ModeMenu extends Component {
|
export default class ModeMenu extends Component {
|
||||||
/**
|
/**
|
||||||
|
@ -63,9 +62,7 @@ export default class ModeMenu extends Component {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!this.handleRequestClose) {
|
if (!this.handleRequestClose) {
|
||||||
this.handleRequestClose = (event) => {
|
this.handleRequestClose = (event) => {
|
||||||
if (!findParentWithAttribute(event.target, 'data-menu', 'true')) {
|
|
||||||
this.props.onRequestClose()
|
this.props.onRequestClose()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
window.addEventListener('click', this.handleRequestClose)
|
window.addEventListener('click', this.handleRequestClose)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue