Completed displaying of context menu
This commit is contained in:
parent
a1859a9aff
commit
a9f0fe07c1
|
@ -3,9 +3,127 @@ import { h, Component } from 'preact'
|
|||
export default class ContextMenu extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
expanded: null, // menu index of expanded menu item
|
||||
expanding: null, // menu index of expanding menu item
|
||||
collapsing: null // menu index of collapsing menu item
|
||||
}
|
||||
|
||||
this.onUpdate = [] // handlers to be executed after component update
|
||||
|
||||
this.renderMenuItem = this.renderMenuItem.bind(this)
|
||||
}
|
||||
|
||||
render () {
|
||||
return h('div', {class: 'jsoneditor-contextmenu'}, 'context menu...')
|
||||
|
||||
// TODO: create a non-visible button to set the focus to the menu
|
||||
// TODO: implement (customizable) quick keys
|
||||
|
||||
// TODO: render the context menu on top when there is no space below the node
|
||||
|
||||
return h('div', {class: 'jsoneditor-contextmenu'},
|
||||
this.props.items.map(this.renderMenuItem)
|
||||
)
|
||||
}
|
||||
|
||||
renderMenuItem (item, index) {
|
||||
if (item.type === 'separator') {
|
||||
return h('div', {class: 'jsoneditor-menu-separator'})
|
||||
}
|
||||
|
||||
if (item.click && item.submenu) {
|
||||
// two buttons: direct click and a small button to expand the submenu
|
||||
return h('div', {class: 'jsoneditor-menu-item'}, [
|
||||
h('button', {class: 'jsoneditor-menu-button jsoneditor-menu-default ' + item.className, title: item.title, onClick: item.click }, [
|
||||
h('span', {class: 'jsoneditor-icon'}),
|
||||
h('span', {class: 'jsoneditor-text'}, item.text)
|
||||
]),
|
||||
h('button', {class: 'jsoneditor-menu-button jsoneditor-menu-expand', onClick: this.createExpandHandler(index) }, [
|
||||
h('span', {class: 'jsoneditor-icon jsoneditor-icon-expand'})
|
||||
]),
|
||||
this.renderSubMenu(item.submenu, index)
|
||||
])
|
||||
}
|
||||
else if (item.submenu) {
|
||||
// button expands the submenu
|
||||
|
||||
return h('div', {class: 'jsoneditor-menu-item'}, [
|
||||
h('button', {class: 'jsoneditor-menu-button ' + item.className, title: item.title, onClick: this.createExpandHandler(index) }, [
|
||||
h('span', {class: 'jsoneditor-icon'}),
|
||||
h('span', {class: 'jsoneditor-text'}, item.text),
|
||||
h('span', {class: 'jsoneditor-icon jsoneditor-icon-expand'}),
|
||||
]),
|
||||
this.renderSubMenu(item.submenu, index)
|
||||
])
|
||||
}
|
||||
else {
|
||||
// just a button (no submenu)
|
||||
return h('div', {class: 'jsoneditor-menu-item'}, [
|
||||
h('button', {class: 'jsoneditor-menu-button ' + item.className, title: item.title, onClick: item.click }, [
|
||||
h('span', {class: 'jsoneditor-icon'}),
|
||||
h('span', {class: 'jsoneditor-text'}, item.text)
|
||||
]),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} submenu
|
||||
* @param {number} index
|
||||
*/
|
||||
renderSubMenu (submenu, index) {
|
||||
const expanding = this.state.expanding === index
|
||||
const collapsing = this.state.collapsing === index
|
||||
|
||||
const contents = submenu.map(item => {
|
||||
return h('div', {class: 'jsoneditor-menu-item'}, [
|
||||
h('button', {class: 'jsoneditor-menu-button ' + item.className, title: item.title, onClick: item.click }, [
|
||||
h('span', {class: 'jsoneditor-icon'}),
|
||||
h('span', {class: 'jsoneditor-text'}, item.text)
|
||||
]),
|
||||
])
|
||||
})
|
||||
|
||||
const className = 'jsoneditor-submenu ' +
|
||||
(expanding ? ' jsoneditor-expanding' : '') +
|
||||
(collapsing ? ' jsoneditor-collapsing' : '')
|
||||
|
||||
return h('div', {class: className}, contents)
|
||||
}
|
||||
|
||||
createExpandHandler (index) {
|
||||
return (event) => {
|
||||
event.stopPropagation()
|
||||
|
||||
const prev = this.state.expanded
|
||||
|
||||
this.setState({
|
||||
expanded: (prev === index) ? null : index,
|
||||
expanding: null,
|
||||
collapsing: prev
|
||||
})
|
||||
|
||||
this.onUpdate.push(() => {
|
||||
this.setState({
|
||||
expanding: this.state.expanded
|
||||
})
|
||||
})
|
||||
|
||||
// timeout after unit is collapsed
|
||||
setTimeout(() => {
|
||||
if (prev === this.state.collapsing) {
|
||||
this.setState({
|
||||
collapsing: null
|
||||
})
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate () {
|
||||
this.onUpdate.forEach(handler => handler())
|
||||
this.onUpdate = []
|
||||
|
||||
}
|
||||
}
|
||||
|
|
217
src/JSONNode.js
217
src/JSONNode.js
|
@ -4,7 +4,21 @@ import ContextMenu from './ContextMenu'
|
|||
import { escapeHTML, unescapeHTML } from './utils/stringUtils'
|
||||
import { getInnerText } from './utils/domUtils'
|
||||
import {stringConvert, valueType, isUrl} from './utils/typeUtils'
|
||||
import * as pointer from 'json-pointer'
|
||||
|
||||
// TYPE_TITLES with explanation for the different types
|
||||
const TYPE_TITLES = {
|
||||
'value': 'Item type "value". ' +
|
||||
'The item type is automatically determined from the value ' +
|
||||
'and can be a string, number, boolean, or null.',
|
||||
'object': 'Item type "object". ' +
|
||||
'An object contains an unordered set of key/value pairs.',
|
||||
'array': 'Item type "array". ' +
|
||||
'An array contains an ordered collection of values.',
|
||||
'string': 'Item type "string". ' +
|
||||
'Item type is not determined from the value, ' +
|
||||
'but always returned as string.'
|
||||
};
|
||||
|
||||
|
||||
export default class JSONNode extends Component {
|
||||
constructor (props) {
|
||||
|
@ -30,7 +44,7 @@ export default class JSONNode extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
renderJSONObject ({data, index, options, events, menu}) {
|
||||
renderJSONObject ({data, index, options, events}) {
|
||||
const childCount = data.childs.length
|
||||
const contents = [
|
||||
h('div', {class: 'jsoneditor-node jsoneditor-object'}, [
|
||||
|
@ -47,8 +61,7 @@ export default class JSONNode extends Component {
|
|||
return h(JSONNode, {
|
||||
data: child,
|
||||
options,
|
||||
events,
|
||||
menu
|
||||
events
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -58,7 +71,7 @@ export default class JSONNode extends Component {
|
|||
return h('li', {}, contents)
|
||||
}
|
||||
|
||||
renderJSONArray ({data, index, options, events, menu}) {
|
||||
renderJSONArray ({data, index, options, events}) {
|
||||
const childCount = data.childs.length
|
||||
const contents = [
|
||||
h('div', {class: 'jsoneditor-node jsoneditor-array'}, [
|
||||
|
@ -76,8 +89,7 @@ export default class JSONNode extends Component {
|
|||
data: child,
|
||||
index,
|
||||
options,
|
||||
events,
|
||||
menu
|
||||
events
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -147,25 +159,187 @@ export default class JSONNode extends Component {
|
|||
}
|
||||
|
||||
renderContextMenuButton () {
|
||||
const visible = this.contextMenuVisible()
|
||||
const childs = visible
|
||||
? [ h(ContextMenu) ]
|
||||
: null
|
||||
const visible = this.props.data.menu === true
|
||||
|
||||
const className = `jsoneditor-button jsoneditor-contextmenu` + (visible ? ' jsoneditor-visible' : '')
|
||||
const className = 'jsoneditor-button jsoneditor-contextmenu' + (visible ? ' jsoneditor-visible' : '')
|
||||
return h('div', {class: 'jsoneditor-button-container'},
|
||||
h('button', {class: className, onClick: this.handleContextMenu}, childs)
|
||||
visible ? this.renderContextMenu() : null,
|
||||
h('button', {class: className, onClick: this.handleContextMenu})
|
||||
)
|
||||
}
|
||||
|
||||
renderContextMenu () {
|
||||
const hasParent = this.props.data.path !== ''
|
||||
const type = this.props.data.type
|
||||
const items = [] // array with menu items
|
||||
|
||||
items.push({
|
||||
text: 'Type',
|
||||
title: 'Change the type of this field',
|
||||
className: 'jsoneditor-type-' + type,
|
||||
submenu: [
|
||||
{
|
||||
text: 'Value',
|
||||
className: 'jsoneditor-type-value' + (type == 'value' ? ' jsoneditor-selected' : ''),
|
||||
title: TYPE_TITLES.value,
|
||||
click: function () {
|
||||
alert('value') // TODO
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Array',
|
||||
className: 'jsoneditor-type-array' + (type == 'array' ? ' jsoneditor-selected' : ''),
|
||||
title: TYPE_TITLES.array,
|
||||
click: function () {
|
||||
alert('array') // TODO
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Object',
|
||||
className: 'jsoneditor-type-object' + (type == 'object' ? ' jsoneditor-selected' : ''),
|
||||
title: TYPE_TITLES.object,
|
||||
click: function () {
|
||||
//node._onChangeType('object');
|
||||
alert('object') // TODO
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'String',
|
||||
className: 'jsoneditor-type-string' + (type == 'string' ? ' jsoneditor-selected' : ''),
|
||||
title: TYPE_TITLES.string,
|
||||
click: function () {
|
||||
// node._onChangeType('string');
|
||||
alert('string') // TODO
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (type === 'array' || type === 'object') {
|
||||
var direction = ((this.sortOrder == 'asc') ? 'desc': 'asc');
|
||||
items.push({
|
||||
text: 'Sort',
|
||||
title: 'Sort the childs of this ' + TYPE_TITLES.type,
|
||||
className: 'jsoneditor-sort-' + direction,
|
||||
click: function () {
|
||||
// node.sort(direction);
|
||||
alert('sort') // TODO
|
||||
},
|
||||
submenu: [
|
||||
{
|
||||
text: 'Ascending',
|
||||
className: 'jsoneditor-sort-asc',
|
||||
title: 'Sort the childs of this ' + TYPE_TITLES.type + ' in ascending order',
|
||||
click: function () {
|
||||
// node.sort('asc');
|
||||
alert('asc') // TODO
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Descending',
|
||||
className: 'jsoneditor-sort-desc',
|
||||
title: 'Sort the childs of this ' + TYPE_TITLES.type +' in descending order',
|
||||
click: function () {
|
||||
// node.sort('desc');
|
||||
alert('desc') // TODO
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
if (hasParent) {
|
||||
if (items.length) {
|
||||
// create a separator
|
||||
items.push({
|
||||
'type': 'separator'
|
||||
});
|
||||
}
|
||||
|
||||
// create insert button
|
||||
items.push({
|
||||
text: 'Insert',
|
||||
title: 'Insert a new item with type \'value\' after this item (Ctrl+Ins)',
|
||||
submenuTitle: 'Select the type of the item to be inserted',
|
||||
className: 'jsoneditor-insert',
|
||||
click: function () {
|
||||
// node._onInsertBefore('', '', 'value');
|
||||
alert('insert') // TODO
|
||||
},
|
||||
submenu: [
|
||||
{
|
||||
text: 'Value',
|
||||
className: 'jsoneditor-type-value',
|
||||
title: TYPE_TITLES.value,
|
||||
click: function () {
|
||||
// node._onInsertBefore('', '', 'value');
|
||||
alert('insert value') // TODO
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Array',
|
||||
className: 'jsoneditor-type-array',
|
||||
title: TYPE_TITLES.array,
|
||||
click: function () {
|
||||
// node._onInsertBefore('', []);
|
||||
alert('insert array') // TODO
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Object',
|
||||
className: 'jsoneditor-type-object',
|
||||
title: TYPE_TITLES.object,
|
||||
click: function () {
|
||||
// node._onInsertBefore('', {});
|
||||
alert('insert object') // TODO
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'String',
|
||||
className: 'jsoneditor-type-string',
|
||||
title: TYPE_TITLES.string,
|
||||
click: function () {
|
||||
// node._onInsertBefore('', '', 'string');
|
||||
alert('insert string') // TODO
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (this.props.path !== '') {
|
||||
// create duplicate button
|
||||
items.push({
|
||||
text: 'Duplicate',
|
||||
title: 'Duplicate this item (Ctrl+D)',
|
||||
className: 'jsoneditor-duplicate',
|
||||
click: function () {
|
||||
// Node.onDuplicate(node);
|
||||
alert('duplicate') // TODO
|
||||
}
|
||||
});
|
||||
|
||||
// create remove button
|
||||
items.push({
|
||||
text: 'Remove',
|
||||
title: 'Remove this item (Ctrl+Del)',
|
||||
className: 'jsoneditor-remove',
|
||||
click: function () {
|
||||
//Node.onRemove(node);
|
||||
alert('remove') // TODO
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: implement a hook to adjust the context menu
|
||||
|
||||
return h(ContextMenu, {items})
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return Object.keys(nextProps).some(prop => this.props[prop] !== nextProps[prop])
|
||||
}
|
||||
|
||||
contextMenuVisible () {
|
||||
return this.props.menu === this.props.data.path
|
||||
}
|
||||
|
||||
static _rootName (data, options) {
|
||||
return typeof options.name === 'string'
|
||||
? options.name
|
||||
|
@ -177,7 +351,10 @@ export default class JSONNode extends Component {
|
|||
handleChangeProperty (event) {
|
||||
const oldProp = this.props.data.prop
|
||||
const newProp = unescapeHTML(getInnerText(event.target))
|
||||
const path = this.props.data.path.replace(/\/.+$/, '') // remove last entry
|
||||
|
||||
// remove last entry from the path to get the path of the parent object
|
||||
const index = this.props.data.path.lastIndexOf('/')
|
||||
const path = this.props.data.path.substr(0, index)
|
||||
|
||||
this.props.events.onChangeProperty(path, oldProp, newProp)
|
||||
}
|
||||
|
@ -205,8 +382,10 @@ export default class JSONNode extends Component {
|
|||
}
|
||||
|
||||
handleContextMenu (event) {
|
||||
event.stopPropagation() // stop propagation, because else Main.js will hide the context menu again
|
||||
|
||||
// toggle visibility of the context menu
|
||||
const path = this.contextMenuVisible()
|
||||
const path = this.props.data.menu === true
|
||||
? null
|
||||
: this.props.data.path
|
||||
|
||||
|
|
69
src/Main.js
69
src/Main.js
|
@ -12,7 +12,7 @@ export default class Main extends Component {
|
|||
this.state = {
|
||||
options: Object.assign({
|
||||
name: null,
|
||||
expand: Main.expand
|
||||
expand: Main.expand // TODO: remove expand as option, should be passed as optional callback to editor.set
|
||||
}, props.options || {}),
|
||||
|
||||
data: {
|
||||
|
@ -23,28 +23,31 @@ export default class Main extends Component {
|
|||
},
|
||||
|
||||
events: {
|
||||
onChangeProperty: this._onChangeProperty.bind(this),
|
||||
onChangeValue: this._onChangeValue.bind(this),
|
||||
onExpand: this._onExpand.bind(this),
|
||||
onContextMenu: this._onContextMenu.bind(this)
|
||||
onChangeProperty: this.handleChangeProperty.bind(this),
|
||||
onChangeValue: this.handleChangeValue.bind(this),
|
||||
onExpand: this.handleExpand.bind(this),
|
||||
onContextMenu: this.handleContextMenu.bind(this)
|
||||
},
|
||||
|
||||
/** @type {string | null} */
|
||||
menu: null, // json pointer to the node having menu visible
|
||||
|
||||
search: null
|
||||
}
|
||||
|
||||
this.handleHideContextMenu = this.handleHideContextMenu.bind(this)
|
||||
}
|
||||
|
||||
render() {
|
||||
return h('div', {class: 'jsoneditor'}, [
|
||||
return h('div', {class: 'jsoneditor', onClick: this.handleHideContextMenu}, [
|
||||
h('ul', {class: 'jsoneditor-list'}, [
|
||||
h(JSONNode, this.state)
|
||||
])
|
||||
])
|
||||
}
|
||||
|
||||
_onChangeValue (path, value) {
|
||||
console.log('_onChangeValue', path, value)
|
||||
handleChangeValue (path, value) {
|
||||
console.log('handleChangeValue', path, value)
|
||||
|
||||
const modelPath = Main._pathToModelPath(this.state.data, Main._parsePath(path)).concat('value')
|
||||
|
||||
|
@ -53,30 +56,28 @@ export default class Main extends Component {
|
|||
})
|
||||
}
|
||||
|
||||
_onChangeProperty (path, oldProp, newProp) {
|
||||
console.log('_onChangeProperty', path, oldProp, newProp)
|
||||
handleChangeProperty (path, oldProp, newProp) {
|
||||
console.log('handleChangeProperty', path, oldProp, newProp)
|
||||
|
||||
const array = pointer.parse(path)
|
||||
const parent = getIn(this.state.data, array)
|
||||
const modelPath = Main._pathToModelPath(this.state.data, pointer.parse(path))
|
||||
const parent = getIn(this.state.data, modelPath)
|
||||
const index = parent.childs.findIndex(child => child.prop === oldProp)
|
||||
|
||||
const newPath = path + '/' + pointer.escape(newProp)
|
||||
const modelPath = Main._pathToModelPath(this.state.data, array).concat(['childs', index])
|
||||
|
||||
let data = this.state.data
|
||||
data = setIn(data, modelPath.concat('path'), newPath)
|
||||
data = setIn(data, modelPath.concat('prop'), newProp)
|
||||
data = setIn(data, modelPath.concat(['childs', index, 'path']), newPath)
|
||||
data = setIn(data, modelPath.concat(['childs', index, 'prop']), newProp)
|
||||
|
||||
this.setState({ data })
|
||||
}
|
||||
|
||||
_onExpand(path, expand) {
|
||||
const modelPath = Main._pathToModelPath(this.state.data, Main._parsePath(path)).concat('expanded')
|
||||
handleExpand(path, expand) {
|
||||
const modelPath = Main._pathToModelPath(this.state.data, Main._parsePath(path))
|
||||
|
||||
console.log('_onExpand', path, modelPath)
|
||||
console.log('handleExpand', path, modelPath)
|
||||
|
||||
this.setState({
|
||||
data: setIn(this.state.data, modelPath, expand)
|
||||
data: setIn(this.state.data, modelPath.concat('expanded'), expand)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -85,12 +86,32 @@ export default class Main extends Component {
|
|||
* @param {string | null} path
|
||||
* @private
|
||||
*/
|
||||
_onContextMenu(path) {
|
||||
handleContextMenu(path) {
|
||||
let data = this.state.data
|
||||
|
||||
// hide previous context menu (if any)
|
||||
if (this.state.menu !== null) {
|
||||
const modelPath = Main._pathToModelPath(this.state.data, Main._parsePath(this.state.menu))
|
||||
data = setIn(data, modelPath.concat('menu'), false)
|
||||
}
|
||||
|
||||
// show new menu
|
||||
if (path !== null) {
|
||||
const modelPath = Main._pathToModelPath(this.state.data, Main._parsePath(path))
|
||||
data = setIn(data, modelPath.concat('menu'), true)
|
||||
}
|
||||
|
||||
this.setState({
|
||||
menu: path || null
|
||||
menu: path, // store path of current menu, just to easily find it next time
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
handleHideContextMenu (event) {
|
||||
// FIXME: find a different way to show/hide the context menu. create a single instance in the Main, pass a reference to it into the JSON nodes?
|
||||
this.handleContextMenu(null)
|
||||
}
|
||||
|
||||
// TODO: comment
|
||||
get () {
|
||||
return Main._modelToJson(this.state.data)
|
||||
|
@ -187,7 +208,7 @@ export default class Main extends Component {
|
|||
}
|
||||
else {
|
||||
return {
|
||||
type: 'auto',
|
||||
type: 'value',
|
||||
path,
|
||||
prop,
|
||||
value
|
||||
|
@ -215,7 +236,7 @@ export default class Main extends Component {
|
|||
return object
|
||||
}
|
||||
else {
|
||||
// type 'auto' or 'string'
|
||||
// type 'value' or 'string'
|
||||
return model.value
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
}
|
||||
|
||||
.jsoneditor-node {
|
||||
/*border: 1px solid #555;*/
|
||||
position: relative;
|
||||
|
||||
font: 14px Arial;
|
||||
|
||||
/* flexbox setup */
|
||||
|
@ -167,13 +168,212 @@ button.jsoneditor-button.jsoneditor-contextmenu.jsoneditor-visible {
|
|||
background-position: -50px -50px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/******************************* Context Menu ******************************/
|
||||
|
||||
div.jsoneditor-contextmenu {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
z-index: 99999;
|
||||
top: 20px;
|
||||
left: 0;
|
||||
|
||||
z-index: 1;
|
||||
left: 18px; /* 20px - 2px where 2px half the difference between 24x24 icons of the menu and the 20x20 icons of the editor */
|
||||
background: white;
|
||||
padding: 5px;
|
||||
border: 1px solid gray;
|
||||
|
||||
border: 1px solid #d3d3d3;
|
||||
box-shadow: 2px 2px 12px rgba(128, 128, 128, 0.3);
|
||||
}
|
||||
|
||||
div.jsoneditor-menu-item {
|
||||
line-height: 0;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
button.jsoneditor-menu-button {
|
||||
width: 136px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
line-height: 24px;
|
||||
|
||||
background: transparent;
|
||||
border: transparent;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
|
||||
cursor: pointer;
|
||||
color: #4d4d4d;
|
||||
|
||||
font-size: 10pt;
|
||||
font-family: arial, sans-serif;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
button.jsoneditor-menu-button:hover,
|
||||
button.jsoneditor-menu-button:focus {
|
||||
color: #1a1a1a;
|
||||
background-color: #f5f5f5;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button.jsoneditor-menu-button.jsoneditor-selected {
|
||||
color: white;
|
||||
background-color: #ee422e;
|
||||
}
|
||||
|
||||
button.jsoneditor-menu-default {
|
||||
width: 104px; /* 136px - 32px */
|
||||
}
|
||||
|
||||
button.jsoneditor-menu-expand {
|
||||
width: 32px;
|
||||
float: right;
|
||||
border-left: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
span.jsoneditor-icon {
|
||||
float: left;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-image: url('img/jsoneditor-icons.svg');
|
||||
}
|
||||
|
||||
span.jsoneditor-icon.jsoneditor-icon-expand {
|
||||
float: right;
|
||||
width: 24px;
|
||||
margin: 0 4px;
|
||||
|
||||
background-position: 0 -72px !important;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu-item button.jsoneditor-menu-button:hover span.jsoneditor-icon-expand,
|
||||
div.jsoneditor-menu-item button:focus span.jsoneditor-icon-expand {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
span.jsoneditor-text {
|
||||
display: inline-block;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
div.jsoneditor-menu-separator {
|
||||
height: 0;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
padding-top: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
button.jsoneditor-remove span.jsoneditor-icon {
|
||||
background-position: -24px -24px;
|
||||
}
|
||||
button.jsoneditor-remove:hover span.jsoneditor-icon,
|
||||
button.jsoneditor-remove:focus span.jsoneditor-icon {
|
||||
background-position: -24px 0;
|
||||
}
|
||||
|
||||
button.jsoneditor-insert span.jsoneditor-icon {
|
||||
background-position: 0 -24px;
|
||||
}
|
||||
button.jsoneditor-insert:hover span.jsoneditor-icon,
|
||||
button.jsoneditor-insert:focus span.jsoneditor-icon {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
button.jsoneditor-duplicate span.jsoneditor-icon {
|
||||
background-position: -48px -24px;
|
||||
}
|
||||
button.jsoneditor-duplicate:hover span.jsoneditor-icon,
|
||||
button.jsoneditor-duplicate:focus span.jsoneditor-icon {
|
||||
background-position: -48px 0;
|
||||
}
|
||||
|
||||
button.jsoneditor-sort-asc span.jsoneditor-icon {
|
||||
background-position: -168px -24px;
|
||||
}
|
||||
button.jsoneditor-sort-asc:hover span.jsoneditor-icon,
|
||||
button.jsoneditor-sort-asc:focus span.jsoneditor-icon {
|
||||
background-position: -168px 0;
|
||||
}
|
||||
|
||||
button.jsoneditor-sort-desc span.jsoneditor-icon {
|
||||
background-position: -192px -24px;
|
||||
}
|
||||
button.jsoneditor-sort-desc:hover span.jsoneditor-icon,
|
||||
button.jsoneditor-sort-desc:focus span.jsoneditor-icon {
|
||||
background-position: -192px 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-submenu {
|
||||
visibility: hidden;
|
||||
max-height: 0;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
transition: max-height 0.3s ease-out;
|
||||
|
||||
box-shadow: inset 0 10px 10px -10px rgba(128, 128, 128, 0.5),
|
||||
inset 0 -10px 10px -10px rgba(128, 128, 128, 0.5)
|
||||
}
|
||||
|
||||
div.jsoneditor-submenu.jsoneditor-expanding {
|
||||
visibility: visible;
|
||||
max-height: 104px; /* 4 * 24px + 2 * 5px */
|
||||
/* FIXME: shouldn't rely on max-height equal to 4 items, should be flexible */
|
||||
}
|
||||
|
||||
div.jsoneditor-submenu.jsoneditor-collapsing {
|
||||
visibility: visible;
|
||||
max-height: 0;
|
||||
}
|
||||
|
||||
div.jsoneditor-submenu button {
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
div.jsoneditor-submenu div.jsoneditor-menu-item:first-child {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
div.jsoneditor-submenu div.jsoneditor-menu-item:last-child {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
button.jsoneditor-type-string span.jsoneditor-icon {
|
||||
background-position: -144px -24px;
|
||||
}
|
||||
button.jsoneditor-type-string:hover span.jsoneditor-icon,
|
||||
button.jsoneditor-type-string:focus span.jsoneditor-icon,
|
||||
button.jsoneditor-type-string.jsoneditor-selected span.jsoneditor-icon {
|
||||
background-position: -144px 0;
|
||||
}
|
||||
|
||||
button.jsoneditor-type-value span.jsoneditor-icon {
|
||||
background-position: -120px -24px;
|
||||
}
|
||||
button.jsoneditor-type-value:hover span.jsoneditor-icon,
|
||||
button.jsoneditor-type-value:focus span.jsoneditor-icon,
|
||||
button.jsoneditor-type-value.jsoneditor-selected span.jsoneditor-icon {
|
||||
background-position: -120px 0;
|
||||
}
|
||||
|
||||
button.jsoneditor-type-object span.jsoneditor-icon {
|
||||
background-position: -72px -24px;
|
||||
}
|
||||
button.jsoneditor-type-object:hover span.jsoneditor-icon,
|
||||
button.jsoneditor-type-object:focus span.jsoneditor-icon,
|
||||
button.jsoneditor-type-object.jsoneditor-selected span.jsoneditor-icon {
|
||||
background-position: -72px 0;
|
||||
}
|
||||
|
||||
button.jsoneditor-type-array span.jsoneditor-icon {
|
||||
background-position: -96px -24px;
|
||||
}
|
||||
button.jsoneditor-type-array:hover span.jsoneditor-icon,
|
||||
button.jsoneditor-type-array:focus span.jsoneditor-icon,
|
||||
button.jsoneditor-type-array.jsoneditor-selected span.jsoneditor-icon {
|
||||
background-position: -96px 0;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* @typedef {{
|
||||
* type: string,
|
||||
* expanded: boolean?,
|
||||
* menu: boolean?,
|
||||
* path: string,
|
||||
* prop: string?,
|
||||
* value: *?,
|
||||
|
|
Loading…
Reference in New Issue