Refactored action menus, added quick keys Ctrl+M, Ctrl+E
This commit is contained in:
parent
bb8734707e
commit
94b7be90d3
|
@ -2,27 +2,20 @@
|
||||||
|
|
||||||
import { createElement as h, Component } from 'react'
|
import { createElement as h, Component } from 'react'
|
||||||
|
|
||||||
import ActionButton from './menu/ActionButton'
|
import ActionMenu from './menu/ActionMenu'
|
||||||
import AppendActionButton from './menu/AppendActionButton'
|
|
||||||
import { escapeHTML, unescapeHTML } from '../utils/stringUtils'
|
import { escapeHTML, unescapeHTML } from '../utils/stringUtils'
|
||||||
import { getInnerText, insideRect } from '../utils/domUtils'
|
import { getInnerText, insideRect, findParentNode } from '../utils/domUtils'
|
||||||
import { stringConvert, valueType, isUrl } from '../utils/typeUtils'
|
import { stringConvert, valueType, isUrl } from '../utils/typeUtils'
|
||||||
import { compileJSONPointer } from '../jsonData'
|
import { compileJSONPointer } from '../jsonData'
|
||||||
|
|
||||||
import type { PropertyData, JSONData, SearchResultStatus } from '../types'
|
import type { PropertyData, JSONData, SearchResultStatus } from '../types'
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {JSONNode | null} activeContextMenu singleton holding the JSONNode having
|
|
||||||
* the active (visible) context menu
|
|
||||||
*/
|
|
||||||
let activeContextMenu = null
|
|
||||||
|
|
||||||
export default class JSONNode extends Component {
|
export default class JSONNode extends Component {
|
||||||
static URL_TITLE = 'Ctrl+Click or Ctrl+Enter to open url'
|
static URL_TITLE = 'Ctrl+Click or Ctrl+Enter to open url'
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
menu: null, // context menu
|
menu: null, // can contain object {anchor, root}
|
||||||
appendMenu: null, // append context menu (used in placeholder of empty object/array)
|
appendMenu: null, // can contain object {anchor, root}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
@ -48,6 +41,7 @@ export default class JSONNode extends Component {
|
||||||
className: 'jsoneditor-node jsoneditor-object'
|
className: 'jsoneditor-node jsoneditor-object'
|
||||||
}, [
|
}, [
|
||||||
this.renderExpandButton(),
|
this.renderExpandButton(),
|
||||||
|
this.renderActionMenu('update', this.state.menu, this.handleCloseActionMenu),
|
||||||
this.renderActionMenuButton(),
|
this.renderActionMenuButton(),
|
||||||
this.renderProperty(prop, index, data, options),
|
this.renderProperty(prop, index, data, options),
|
||||||
this.renderReadonly(`{${childCount}}`, `Array containing ${childCount} items`),
|
this.renderReadonly(`{${childCount}}`, `Array containing ${childCount} items`),
|
||||||
|
@ -93,6 +87,7 @@ export default class JSONNode extends Component {
|
||||||
className: 'jsoneditor-node jsoneditor-array'
|
className: 'jsoneditor-node jsoneditor-array'
|
||||||
}, [
|
}, [
|
||||||
this.renderExpandButton(),
|
this.renderExpandButton(),
|
||||||
|
this.renderActionMenu('update', this.state.menu, this.handleCloseActionMenu),
|
||||||
this.renderActionMenuButton(),
|
this.renderActionMenuButton(),
|
||||||
this.renderProperty(prop, index, data, options),
|
this.renderProperty(prop, index, data, options),
|
||||||
this.renderReadonly(`[${childCount}]`, `Array containing ${childCount} items`),
|
this.renderReadonly(`[${childCount}]`, `Array containing ${childCount} items`),
|
||||||
|
@ -134,6 +129,7 @@ export default class JSONNode extends Component {
|
||||||
className: 'jsoneditor-node'
|
className: 'jsoneditor-node'
|
||||||
}, [
|
}, [
|
||||||
this.renderPlaceholder(),
|
this.renderPlaceholder(),
|
||||||
|
this.renderActionMenu('update', this.state.menu, this.handleCloseActionMenu),
|
||||||
this.renderActionMenuButton(),
|
this.renderActionMenuButton(),
|
||||||
this.renderProperty(prop, index, data, options),
|
this.renderProperty(prop, index, data, options),
|
||||||
this.renderSeparator(),
|
this.renderSeparator(),
|
||||||
|
@ -154,7 +150,8 @@ export default class JSONNode extends Component {
|
||||||
onKeyDown: this.handleKeyDownAppend
|
onKeyDown: this.handleKeyDownAppend
|
||||||
}, [
|
}, [
|
||||||
this.renderPlaceholder(),
|
this.renderPlaceholder(),
|
||||||
this.renderAppendMenuButton(),
|
this.renderActionMenu('append', this.state.appendMenu, this.handleCloseAppendActionMenu),
|
||||||
|
this.renderAppendActionMenuButton(),
|
||||||
this.renderReadonly(text)
|
this.renderReadonly(text)
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
@ -167,6 +164,7 @@ export default class JSONNode extends Component {
|
||||||
return h('div', {key: 'readonly', className: 'jsoneditor-readonly', title}, text)
|
return h('div', {key: 'readonly', className: 'jsoneditor-readonly', title}, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: simplify the method renderProperty
|
||||||
renderProperty (prop: ?PropertyData, index: ?number, data: JSONData, options: {escapeUnicode: boolean, isPropertyEditable: (path: string) => boolean}) {
|
renderProperty (prop: ?PropertyData, index: ?number, data: JSONData, options: {escapeUnicode: boolean, isPropertyEditable: (path: string) => boolean}) {
|
||||||
const isIndex = typeof index === 'number'
|
const isIndex = typeof index === 'number'
|
||||||
|
|
||||||
|
@ -368,21 +366,87 @@ export default class JSONNode extends Component {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderActionMenuButton () {
|
// TODO: simplify code for the two action menus
|
||||||
return h(ActionButton, {
|
renderActionMenu (menuType, menuState, onClose) {
|
||||||
key: 'action',
|
if (!menuState) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return h(ActionMenu, {
|
||||||
|
key: 'menu',
|
||||||
path: this.props.path,
|
path: this.props.path,
|
||||||
|
events: this.props.events,
|
||||||
type: this.props.data.type,
|
type: this.props.data.type,
|
||||||
events: this.props.events
|
|
||||||
|
menuType,
|
||||||
|
open: true,
|
||||||
|
anchor: menuState.anchor,
|
||||||
|
root: menuState.root,
|
||||||
|
|
||||||
|
onRequestClose: onClose
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
renderAppendMenuButton () {
|
renderActionMenuButton () {
|
||||||
return h(AppendActionButton, {
|
const className = 'jsoneditor-button jsoneditor-actionmenu' +
|
||||||
key: 'append',
|
((this.state.open) ? ' jsoneditor-visible' : '')
|
||||||
path: this.props.path,
|
|
||||||
events: this.props.events
|
return h('div', {className: 'jsoneditor-button-container', key: 'action'}, [
|
||||||
|
h('button', {
|
||||||
|
key: 'button',
|
||||||
|
ref: 'actionMenuButton',
|
||||||
|
className,
|
||||||
|
onClick: this.handleOpenActionMenu
|
||||||
})
|
})
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAppendActionMenuButton () {
|
||||||
|
const className = 'jsoneditor-button jsoneditor-actionmenu' +
|
||||||
|
((this.state.appendOpen) ? ' jsoneditor-visible' : '')
|
||||||
|
|
||||||
|
return h('div', {className: 'jsoneditor-button-container', key: 'action'}, [
|
||||||
|
h('button', {
|
||||||
|
key: 'button',
|
||||||
|
ref: 'appendActionMenuButton',
|
||||||
|
className,
|
||||||
|
onClick: this.handleOpenAppendActionMenu
|
||||||
|
})
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOpenActionMenu = (event) => {
|
||||||
|
// TODO: don't use refs, find root purely via DOM?
|
||||||
|
const root = findParentNode(this.refs.actionMenuButton, 'data-jsoneditor', 'true')
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
menu: {
|
||||||
|
open: true,
|
||||||
|
anchor: this.refs.actionMenuButton,
|
||||||
|
root
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCloseActionMenu = () => {
|
||||||
|
this.setState({ menu: null })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOpenAppendActionMenu = (event) => {
|
||||||
|
// TODO: don't use refs, find root purely via DOM?
|
||||||
|
const root = findParentNode(this.refs.appendActionMenuButton, 'data-jsoneditor', 'true')
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
appendMenu: {
|
||||||
|
open: true,
|
||||||
|
anchor: this.refs.actionMenuButton,
|
||||||
|
root
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCloseAppendActionMenu = () => {
|
||||||
|
this.setState({ appendMenu: null })
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate (nextProps, nextState) {
|
shouldComponentUpdate (nextProps, nextState) {
|
||||||
|
@ -456,6 +520,18 @@ export default class JSONNode extends Component {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
this.props.events.onRemove(this.props.path)
|
this.props.events.onRemove(this.props.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (keyBinding === 'expand') {
|
||||||
|
event.preventDefault()
|
||||||
|
const recurse = false
|
||||||
|
const expanded = !this.props.data.expanded
|
||||||
|
this.props.events.onExpand(this.props.path, expanded, recurse)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyBinding === 'actionMenu') {
|
||||||
|
event.preventDefault()
|
||||||
|
this.handleOpenActionMenu(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
|
@ -466,6 +542,11 @@ export default class JSONNode extends Component {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
this.props.events.onAppend(this.props.path, 'value')
|
this.props.events.onAppend(this.props.path, 'value')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (keyBinding === 'actionMenu') {
|
||||||
|
event.preventDefault()
|
||||||
|
this.handleOpenAppendActionMenu(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
|
@ -485,20 +566,6 @@ export default class JSONNode extends Component {
|
||||||
this.props.events.onExpand(this.props.path, expanded, recurse)
|
this.props.events.onExpand(this.props.path, expanded, recurse)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Singleton function to hide the currently visible context menu if any.
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
static hideActionMenu () {
|
|
||||||
if (activeContextMenu) {
|
|
||||||
activeContextMenu.setState({
|
|
||||||
menu: null,
|
|
||||||
appendMenu: null
|
|
||||||
})
|
|
||||||
activeContextMenu = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When this JSONNode holds an URL as value, open this URL in a new browser tab
|
* When this JSONNode holds an URL as value, open this URL in a new browser tab
|
||||||
* @param event
|
* @param event
|
||||||
|
|
|
@ -38,6 +38,28 @@ const AJV_OPTIONS = {
|
||||||
const MAX_HISTORY_ITEMS = 1000 // maximum number of undo/redo items to be kept in memory
|
const MAX_HISTORY_ITEMS = 1000 // maximum number of undo/redo items to be kept in memory
|
||||||
const SEARCH_DEBOUNCE = 300 // milliseconds
|
const SEARCH_DEBOUNCE = 300 // milliseconds
|
||||||
|
|
||||||
|
// TODO: make key bindings configurable
|
||||||
|
const KEY_BINDINGS = {
|
||||||
|
'duplicate': ['Ctrl+D', 'Command+D'],
|
||||||
|
'insert': ['Ctrl+Insert', 'Command+Insert'],
|
||||||
|
'remove': ['Ctrl+Delete', 'Command+Delete'],
|
||||||
|
'expand': ['Ctrl+E', 'Command+E'],
|
||||||
|
'actionMenu':['Ctrl+M', 'Command+M'],
|
||||||
|
'up': ['Alt+Up', 'Option+Up'],
|
||||||
|
'down': ['Alt+Down', 'Option+Down'],
|
||||||
|
'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+F Find
|
||||||
|
// F3, Ctrl+G Find next
|
||||||
|
// Shift+F3, Ctrl+Shift+G Find previous
|
||||||
|
// Ctrl+Z Undo last action
|
||||||
|
// Ctrl+Shift+Z Redo
|
||||||
|
}
|
||||||
|
|
||||||
export default class TreeMode extends Component {
|
export default class TreeMode extends Component {
|
||||||
id: number
|
id: number
|
||||||
state: Object
|
state: Object
|
||||||
|
@ -49,30 +71,6 @@ export default class TreeMode extends Component {
|
||||||
|
|
||||||
this.id = Math.round(Math.random() * 1e5) // TODO: create a uuid here?
|
this.id = Math.round(Math.random() * 1e5) // TODO: create a uuid here?
|
||||||
|
|
||||||
// TODO: make key bindings configurable
|
|
||||||
const keyBindings = {
|
|
||||||
'duplicate': ['Ctrl+D', 'Command+D'],
|
|
||||||
'insert': ['Ctrl+Insert', 'Command+Insert'],
|
|
||||||
'remove': ['Ctrl+Delete', 'Command+Delete'],
|
|
||||||
'up': ['Alt+Up', 'Option+Up'],
|
|
||||||
'down': ['Alt+Down', 'Option+Down'],
|
|
||||||
'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+Ins Insert a new field with type auto
|
|
||||||
// Ctrl+Shift+Ins Append a new field with type auto
|
|
||||||
// Ctrl+E Expand or collapse field
|
|
||||||
// Ctrl+F Find
|
|
||||||
// F3, Ctrl+G Find next
|
|
||||||
// Shift+F3, Ctrl+Shift+G Find previous
|
|
||||||
// Ctrl+M Show actions menu
|
|
||||||
// Ctrl+Z Undo last action
|
|
||||||
// Ctrl+Shift+Z Redo
|
|
||||||
}
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
data,
|
data,
|
||||||
|
|
||||||
|
@ -100,7 +98,7 @@ export default class TreeMode extends Component {
|
||||||
active: null // active search result
|
active: null // active search result
|
||||||
},
|
},
|
||||||
|
|
||||||
keyCombos: this.bindingsByCombos (keyBindings)
|
keyCombos: this.bindingsByCombos (KEY_BINDINGS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +173,7 @@ export default class TreeMode extends Component {
|
||||||
key: 'contents',
|
key: 'contents',
|
||||||
ref: 'contents',
|
ref: 'contents',
|
||||||
className: 'jsoneditor-contents jsoneditor-tree-contents',
|
className: 'jsoneditor-contents jsoneditor-tree-contents',
|
||||||
onClick: this.handleHideMenus, id: this.id
|
id: this.id
|
||||||
},
|
},
|
||||||
h('ul', {className: 'jsoneditor-list jsoneditor-root'},
|
h('ul', {className: 'jsoneditor-list jsoneditor-root'},
|
||||||
h(Node, {
|
h(Node, {
|
||||||
|
@ -322,11 +320,6 @@ export default class TreeMode extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @private */
|
|
||||||
handleHideMenus = () => {
|
|
||||||
JSONNode.hideActionMenu()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
handleChangeValue = (path, value) => {
|
handleChangeValue = (path, value) => {
|
||||||
this.handlePatch(changeValue(this.state.data, path, value))
|
this.handlePatch(changeValue(this.state.data, path, value))
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
import { createElement as h, Component } from 'react'
|
|
||||||
import ActionMenu from './ActionMenu'
|
|
||||||
import { findParentNode } from '../../utils/domUtils'
|
|
||||||
|
|
||||||
export default class ActionButton extends Component {
|
|
||||||
constructor (props) {
|
|
||||||
super (props)
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
open: false, // whether the menu is open or not
|
|
||||||
anchor: null,
|
|
||||||
root: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {{path, type, events}} props
|
|
||||||
* @param state
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
render () {
|
|
||||||
const { props, state} = this
|
|
||||||
|
|
||||||
const className = 'jsoneditor-button jsoneditor-actionmenu' +
|
|
||||||
(this.state.open ? ' jsoneditor-visible' : '')
|
|
||||||
|
|
||||||
return h('div', {className: 'jsoneditor-button-container'}, [
|
|
||||||
h(ActionMenu, {
|
|
||||||
key: 'menu',
|
|
||||||
...props, // path, type, events
|
|
||||||
...state, // open, anchor, root
|
|
||||||
onRequestClose: this.handleRequestClose
|
|
||||||
}),
|
|
||||||
h('button', {
|
|
||||||
key: 'button',
|
|
||||||
className,
|
|
||||||
onClick: this.handleOpen
|
|
||||||
})
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
handleOpen = (event) => {
|
|
||||||
this.setState({
|
|
||||||
open: true,
|
|
||||||
anchor: event.target,
|
|
||||||
root: findParentNode(event.target, 'data-jsoneditor', 'true')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
handleRequestClose = () => {
|
|
||||||
this.setState({open: false})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,17 +3,26 @@ import Menu from './Menu'
|
||||||
import {
|
import {
|
||||||
createChangeType, createSort,
|
createChangeType, createSort,
|
||||||
createSeparator,
|
createSeparator,
|
||||||
createInsert, createDuplicate, createRemove
|
createInsert, createAppend, createDuplicate, createRemove
|
||||||
} from './entries'
|
} from './items'
|
||||||
|
|
||||||
export default class ActionMenu extends Component {
|
export default class ActionMenu extends Component {
|
||||||
/**
|
/**
|
||||||
* @param {{open, anchor, root, path, type, events, onRequestClose}} props
|
* props = {open, anchor, root, path, type, menuType, events, onRequestClose}
|
||||||
* @param state
|
|
||||||
* @return {JSX.Element}
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { props, state} = this
|
const items = this.props.menuType === 'append' // update or append
|
||||||
|
? this.createAppendMenuItems()
|
||||||
|
: this.createActionMenuItems()
|
||||||
|
|
||||||
|
// TODO: implement a hook to adjust the action menu items
|
||||||
|
|
||||||
|
return h(Menu, { ...this.props, items })
|
||||||
|
}
|
||||||
|
|
||||||
|
createActionMenuItems () {
|
||||||
|
const props = this.props
|
||||||
|
|
||||||
let items = [] // array with menu items
|
let items = [] // array with menu items
|
||||||
|
|
||||||
|
@ -33,11 +42,12 @@ export default class ActionMenu extends Component {
|
||||||
items.push(createRemove(props.path, props.events.onRemove))
|
items.push(createRemove(props.path, props.events.onRemove))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement a hook to adjust the action menu
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
return h(Menu, {
|
createAppendMenuItems () {
|
||||||
...props,
|
return [
|
||||||
items
|
createAppend(this.props.path, this.props.events.onAppend)
|
||||||
})
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
import { createElement as h, Component } from 'react'
|
|
||||||
import AppendActionMenu from './AppendActionMenu'
|
|
||||||
import { findParentNode } from '../../utils/domUtils'
|
|
||||||
|
|
||||||
export default class AppendActionButton extends Component {
|
|
||||||
constructor (props) {
|
|
||||||
super (props)
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
open: false, // whether the menu is open or not
|
|
||||||
anchor: null,
|
|
||||||
root: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {{path, events}} props
|
|
||||||
* @param state
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
render () {
|
|
||||||
const { props, state} = this
|
|
||||||
|
|
||||||
const className = 'jsoneditor-button jsoneditor-actionmenu' +
|
|
||||||
(this.state.open ? ' jsoneditor-visible' : '')
|
|
||||||
|
|
||||||
return h('div', {className: 'jsoneditor-button-container'}, [
|
|
||||||
h(AppendActionMenu, {
|
|
||||||
key: 'menu',
|
|
||||||
...props, // path, events
|
|
||||||
...state, // open, anchor, root
|
|
||||||
onRequestClose: this.handleRequestClose
|
|
||||||
}),
|
|
||||||
h('button', {
|
|
||||||
key: 'button',
|
|
||||||
className: className,
|
|
||||||
onClick: this.handleOpen
|
|
||||||
})
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
handleOpen = (event) => {
|
|
||||||
this.setState({
|
|
||||||
open: true,
|
|
||||||
anchor: event.target,
|
|
||||||
root: findParentNode(event.target, 'data-jsoneditor', 'true')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
handleRequestClose = () => {
|
|
||||||
this.setState({open: false})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
import { createElement as h, Component } from 'react'
|
|
||||||
import Menu from './Menu'
|
|
||||||
import { createAppend } from './entries'
|
|
||||||
|
|
||||||
export default class AppendActionMenu extends Component {
|
|
||||||
/**
|
|
||||||
* @param {{anchor, root, path, events}} props
|
|
||||||
* @param state
|
|
||||||
* @return {JSX.Element}
|
|
||||||
*/
|
|
||||||
render () {
|
|
||||||
const { props, state} = this
|
|
||||||
|
|
||||||
const items = [
|
|
||||||
createAppend(props.path, props.events.onAppend)
|
|
||||||
]
|
|
||||||
|
|
||||||
// TODO: implement a hook to adjust the action menu
|
|
||||||
|
|
||||||
return h(Menu, {
|
|
||||||
...props,
|
|
||||||
items
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,6 +4,10 @@ import { findParentNode } from '../../utils/domUtils'
|
||||||
export let CONTEXT_MENU_HEIGHT = 240
|
export let CONTEXT_MENU_HEIGHT = 240
|
||||||
|
|
||||||
export default class Menu extends Component {
|
export default class Menu extends Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{open: boolean, items: Array, anchor, root, onRequestClose: function}} props
|
||||||
|
*/
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
|
@ -14,11 +18,6 @@ export default class Menu extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {{open: boolean, items: Array, anchor, root, onRequestClose: function}} props
|
|
||||||
* @param state
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
render () {
|
render () {
|
||||||
if (!this.props.open) {
|
if (!this.props.open) {
|
||||||
return null
|
return null
|
||||||
|
@ -159,7 +158,8 @@ export default class Menu extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
this.removeRequestCloseListener()
|
// remove on next tick, since a listener can be created on next tick too
|
||||||
|
setTimeout(() => this.removeRequestCloseListener())
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRequestCloseListener () {
|
updateRequestCloseListener () {
|
||||||
|
@ -172,18 +172,14 @@ export default class Menu extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
addRequestCloseListener () {
|
addRequestCloseListener () {
|
||||||
if (!this.handleRequestClose) {
|
|
||||||
// Attach event listener on next tick, else the current click to open
|
// Attach event listener on next tick, else the current click to open
|
||||||
// the menu will immediately result in requestClose event as well
|
// the menu will immediately result in requestClose event as well
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.handleRequestClose = (event) => {
|
if (this.props.open && !this.handleRequestClose) {
|
||||||
if (!findParentNode(event.target, 'data-menu', 'true')) {
|
this.handleRequestClose = (event) => this.props.onRequestClose()
|
||||||
this.props.onRequestClose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
window.addEventListener('click', this.handleRequestClose)
|
window.addEventListener('click', this.handleRequestClose)
|
||||||
}, 0)
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
removeRequestCloseListener () {
|
removeRequestCloseListener () {
|
||||||
|
|
|
@ -22,25 +22,25 @@ export function createChangeType (path, type, onChangeType) {
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
text: 'Value',
|
text: 'Value',
|
||||||
className: 'jsoneditor-type-value' + (type == 'value' ? ' jsoneditor-selected' : ''),
|
className: 'jsoneditor-type-value' + (type === 'value' ? ' jsoneditor-selected' : ''),
|
||||||
title: TYPE_TITLES.value,
|
title: TYPE_TITLES.value,
|
||||||
click: () => onChangeType(path, 'value')
|
click: () => onChangeType(path, 'value')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Array',
|
text: 'Array',
|
||||||
className: 'jsoneditor-type-Array' + (type == 'Array' ? ' jsoneditor-selected' : ''),
|
className: 'jsoneditor-type-Array' + (type === 'Array' ? ' jsoneditor-selected' : ''),
|
||||||
title: TYPE_TITLES.array,
|
title: TYPE_TITLES.array,
|
||||||
click: () => onChangeType(path, 'Array')
|
click: () => onChangeType(path, 'Array')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Object',
|
text: 'Object',
|
||||||
className: 'jsoneditor-type-Object' + (type == 'Object' ? ' jsoneditor-selected' : ''),
|
className: 'jsoneditor-type-Object' + (type === 'Object' ? ' jsoneditor-selected' : ''),
|
||||||
title: TYPE_TITLES.object,
|
title: TYPE_TITLES.object,
|
||||||
click: () => onChangeType(path, 'Object')
|
click: () => onChangeType(path, 'Object')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'String',
|
text: 'String',
|
||||||
className: 'jsoneditor-type-string' + (type == 'string' ? ' jsoneditor-selected' : ''),
|
className: 'jsoneditor-type-string' + (type === 'string' ? ' jsoneditor-selected' : ''),
|
||||||
title: TYPE_TITLES.string,
|
title: TYPE_TITLES.string,
|
||||||
click: () => onChangeType(path, 'string')
|
click: () => onChangeType(path, 'string')
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ export function createChangeType (path, type, onChangeType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createSort (path, order, onSort) {
|
export function createSort (path, order, onSort) {
|
||||||
var direction = ((order == 'asc') ? 'desc': 'asc')
|
const direction = ((order === 'asc') ? 'desc': 'asc')
|
||||||
return {
|
return {
|
||||||
text: 'Sort',
|
text: 'Sort',
|
||||||
title: 'Sort the childs of this ' + TYPE_TITLES.type,
|
title: 'Sort the childs of this ' + TYPE_TITLES.type,
|
|
@ -1,4 +1,4 @@
|
||||||
import { selectContentEditable, getSelection as getDOMSelection } from '../../utils/domUtils'
|
import { selectContentEditable } from '../../utils/domUtils'
|
||||||
|
|
||||||
// singleton
|
// singleton
|
||||||
let lastInputName = null
|
let lastInputName = null
|
||||||
|
|
Loading…
Reference in New Issue