Refactor ModeDropDown
This commit is contained in:
parent
0349f94ead
commit
2c560264c0
|
@ -1,56 +0,0 @@
|
|||
import { createElement as h, Component } from 'react'
|
||||
import ModeSelector from './ModeSelector'
|
||||
import { toCapital } from '../../utils/stringUtils'
|
||||
|
||||
import fontawesome from '@fortawesome/fontawesome'
|
||||
import faChevronDown from '@fortawesome/fontawesome-free-solid/faChevronDown'
|
||||
|
||||
import './Menu.css'
|
||||
|
||||
import './Search.css'
|
||||
|
||||
fontawesome.library.add(faChevronDown)
|
||||
|
||||
export default class ModeButton extends Component {
|
||||
constructor (props) {
|
||||
super (props)
|
||||
|
||||
this.state = {
|
||||
open: false // whether the menu is open or not
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* props {{modes: string[], mode: string, onChangeMode: function, onError: function}}
|
||||
*/
|
||||
render () {
|
||||
const { props, state} = this
|
||||
|
||||
return h('div', {className: 'jsoneditor-modes'}, [
|
||||
h('button', {
|
||||
key: 'button',
|
||||
className: 'current-mode',
|
||||
title: 'Switch mode',
|
||||
onClick: this.handleOpen
|
||||
}, [
|
||||
toCapital(props.mode) + ' ',
|
||||
h('i', { key: 'icon', className: 'fa fa-chevron-down' })
|
||||
]),
|
||||
|
||||
h(ModeSelector, {
|
||||
key: 'menu',
|
||||
...props,
|
||||
open: state.open,
|
||||
onRequestClose: this.handleRequestClose
|
||||
})
|
||||
])
|
||||
}
|
||||
|
||||
handleOpen = () => {
|
||||
this.setState({open: true})
|
||||
}
|
||||
|
||||
handleRequestClose = () => {
|
||||
this.setState({open: false})
|
||||
}
|
||||
}
|
|
@ -1,16 +1,68 @@
|
|||
import { createElement as h, Component } from 'react'
|
||||
import { toCapital } from '../../utils/stringUtils'
|
||||
|
||||
import fontawesome from '@fortawesome/fontawesome'
|
||||
import faChevronDown from '@fortawesome/fontawesome-free-solid/faChevronDown'
|
||||
import { keyComboFromEvent } from '../../utils/keyBindings'
|
||||
|
||||
import './Menu.css'
|
||||
|
||||
fontawesome.library.add(faChevronDown)
|
||||
|
||||
const MENU_CLASS_NAME = 'jsoneditor-actionmenu'
|
||||
const MODE_MENU_CLASS_NAME = MENU_CLASS_NAME + ' jsoneditor-modemenu'
|
||||
|
||||
export default class ModeSelector extends Component {
|
||||
export default class ModeDropDown extends Component {
|
||||
constructor (props) {
|
||||
super (props)
|
||||
|
||||
this.state = {
|
||||
open: false // whether the menu is open or not
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.removeRequestCloseListener()
|
||||
}
|
||||
|
||||
/**
|
||||
* props {{modes: string[], mode: string, onChangeMode: function, onError: function}}
|
||||
*/
|
||||
render () {
|
||||
return h('div', {className: 'jsoneditor-modes'}, [
|
||||
h('button', {
|
||||
key: 'button',
|
||||
className: 'current-mode',
|
||||
title: 'Switch mode',
|
||||
onClick: this.handleOpen
|
||||
}, [
|
||||
toCapital(this.props.mode) + ' ',
|
||||
h('i', { key: 'icon', className: 'fa fa-chevron-down' })
|
||||
]),
|
||||
|
||||
this.renderDropDown()
|
||||
])
|
||||
}
|
||||
|
||||
handleOpen = () => {
|
||||
this.setState({open: true})
|
||||
|
||||
this.addRequestCloseListener()
|
||||
|
||||
setTimeout(() => this.focusToFirstEntry())
|
||||
}
|
||||
|
||||
handleRequestClose = () => {
|
||||
this.setState({open: false})
|
||||
|
||||
this.removeRequestCloseListener()
|
||||
}
|
||||
|
||||
/**
|
||||
* {{open, modes, mode, onChangeMode, onRequestClose, onError}} props
|
||||
*/
|
||||
render () {
|
||||
if (this.props.open) {
|
||||
renderDropDown () {
|
||||
if (this.state.open) {
|
||||
const items = this.props.modes.map(mode => {
|
||||
return h('button', {
|
||||
key: mode,
|
||||
|
@ -18,22 +70,20 @@ export default class ModeSelector extends Component {
|
|||
className: 'jsoneditor-menu-button jsoneditor-type-modes' +
|
||||
((mode === this.props.mode) ? ' jsoneditor-selected' : ''),
|
||||
onClick: () => {
|
||||
this.props.onRequestClose()
|
||||
|
||||
// send the onChangeMode on the next tick, after onRequestClose is executed and applied
|
||||
setTimeout(() => {
|
||||
try {
|
||||
this.handleRequestClose()
|
||||
|
||||
this.props.onChangeMode(mode)
|
||||
}
|
||||
catch (err) {
|
||||
this.props.onError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, toCapital(mode))
|
||||
})
|
||||
|
||||
return h('div', {
|
||||
key: 'dropdown',
|
||||
className: MODE_MENU_CLASS_NAME,
|
||||
ref: 'menu',
|
||||
onKeyDown: this.handleKeyDown
|
||||
|
@ -44,65 +94,6 @@ export default class ModeSelector extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.updateRequestCloseListener()
|
||||
|
||||
if (this.props.open) {
|
||||
this.focusToFirstEntry ()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
this.updateRequestCloseListener()
|
||||
|
||||
if (this.props.open && !prevProps.open) {
|
||||
this.focusToFirstEntry ()
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
// remove on next tick, since a listener can be created on next tick too
|
||||
setTimeout(() => this.removeRequestCloseListener())
|
||||
}
|
||||
|
||||
updateRequestCloseListener () {
|
||||
if (this.props.open) {
|
||||
this.addRequestCloseListener()
|
||||
}
|
||||
else {
|
||||
this.removeRequestCloseListener()
|
||||
}
|
||||
}
|
||||
|
||||
addRequestCloseListener () {
|
||||
// Attach event listener on next tick, else the current click to open
|
||||
// the menu will immediately result in requestClose event as well
|
||||
setTimeout(() => {
|
||||
if (!this.handleRequestClose) {
|
||||
this.handleRequestClose = (event) => {
|
||||
this.props.onRequestClose()
|
||||
}
|
||||
window.addEventListener('click', this.handleRequestClose)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
removeRequestCloseListener () {
|
||||
if (this.handleRequestClose) {
|
||||
window.removeEventListener('click', this.handleRequestClose)
|
||||
this.handleRequestClose = null
|
||||
}
|
||||
}
|
||||
|
||||
focusToFirstEntry () {
|
||||
if (this.refs.menu) {
|
||||
const firstButton = this.refs.menu.querySelector('button')
|
||||
if (firstButton) {
|
||||
firstButton.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyDown = (event) => {
|
||||
const combo = keyComboFromEvent (event)
|
||||
|
||||
|
@ -123,5 +114,32 @@ export default class ModeSelector extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
handleRequestClose = null
|
||||
addRequestCloseListener () {
|
||||
// Attach event listener on next tick, else the current click to open
|
||||
// the menu will immediately result in requestClose event as well
|
||||
setTimeout(() => {
|
||||
if (!this.handleWindowRequestClose) {
|
||||
this.handleWindowRequestClose = this.handleRequestClose
|
||||
window.addEventListener('click', this.handleWindowRequestClose)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
removeRequestCloseListener () {
|
||||
if (this.handleWindowRequestClose) {
|
||||
window.removeEventListener('click', this.handleWindowRequestClose)
|
||||
this.handleWindowRequestClose = null
|
||||
}
|
||||
}
|
||||
|
||||
focusToFirstEntry () {
|
||||
if (this.refs.menu) {
|
||||
const firstButton = this.refs.menu.querySelector('button')
|
||||
if (firstButton) {
|
||||
firstButton.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleWindowRequestClose = null
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { createElement as h, PureComponent } from 'react'
|
||||
import ModeButton from './ModeButton'
|
||||
import ModeDropDown from './ModeDropDown'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import fontawesome from '@fortawesome/fontawesome'
|
||||
|
@ -30,7 +30,7 @@ export default class TreeModeMenu extends PureComponent {
|
|||
if (this.props.modes ) {
|
||||
items = items.concat([
|
||||
h('div', {className: 'jsoneditor-menu-group', key: 'mode'}, [
|
||||
h(ModeButton, {
|
||||
h(ModeDropDown, {
|
||||
key: 'mode',
|
||||
modes: this.props.modes,
|
||||
mode: this.props.mode,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { createElement as h, PureComponent } from 'react'
|
||||
import ModeButton from './ModeButton'
|
||||
import ModeDropDown from './ModeDropDown'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import fontawesome from '@fortawesome/fontawesome'
|
||||
|
@ -69,7 +69,7 @@ export default class TreeModeMenu extends PureComponent {
|
|||
if (this.props.modes ) {
|
||||
items = items.concat([
|
||||
h('div', {className: 'jsoneditor-menu-group', key: 'mode'}, [
|
||||
h(ModeButton, {
|
||||
h(ModeDropDown, {
|
||||
key: 'mode',
|
||||
modes: this.props.modes,
|
||||
mode: this.props.mode,
|
||||
|
|
Loading…
Reference in New Issue