jsoneditor
jsoneditor copied to clipboard
Attach custom classnames to the DOM root of a node instead of nested inside
Another small suggestion for a neater design! Would be fantastic for the onCreateMenu callback to be able to return undefined
and have the rendering of the context menu icon skipped or set to display:none
. This way the tree view can more easily convey at a glance which of the elements may have context actions for those of us that are replacing the menu with our own actions.
JSONEditorOptions.onCreateMenu?: ((menuItems: MenuItem[], node: MenuItemNode) => MenuItem[]) | undefined
=>
JSONEditorOptions.onCreateMenu?: ((menuItems: MenuItem[], node: MenuItemNode) => MenuItem[] | undefined) | undefined
Alternatively, an isEmpty check of the MenuItem array would do the same job.
Cheers!
Thanks for your suggestion Alberto. A node with an empty context sounds like you want to make some nodes read-only. I guess it makes most sense in those cases to disable or not show the context menu button in such cases. That would not be trivial though: currently the onCreateMenu
is only called after clicking a context menu button, not when rendering all those buttons.
An easy step could be to show the context menu with a text like "no actions available" when there are no menu items.
Can you explain a bit more about the cases where you want to have nodes completely without context menu?
Have you looked into the option onEditable
which also allows to disable many of the menu items?
I'm more interested in disabling the button as a way to signal to the user which elements do have action items. This is certainly an edge case so an example is likely in order:
I am using onEditable
to whitelist elements such as each individual connection (to allow reordering and removal), and have used onCreateMenu
to create my own list of insertion elements. In the example above is an array of connections. The connection object itself is not editable, however does have an Add Connection
action in the context menu. Each individual connection is editable, and therefore I keep the default Delete
action as the sole action in their context menu. The rest of the elements have no context menu items after my functional filtering, but the user can't tell that at a glance; they'll only know which element can be inserted into or deleted by clicking each one, which is less than ideal. Basically I'm trying to replicate a structural edit flag but on two levels (parent-child).
Hopefully that makes my idea make more sense, and again great work on this editor!
Thanks, yes that helps understand where you come from.
Just thinking aloud: maybe you can use onClassName
to give some nodes a class like "nocontextmenu" or so, and then with CSS make the context menu button readonly using for example pointer-events: none;
?
Yes, I had that same thought. I played around with CSS display
trickle down but didn't get too far. Will likely revisit that attempt sometime this weekend.
I gave it a try and I now understand why it doesn't work. The DOM tree looks like:
tr.jsoneditor-expandable.jsoneditor-collapsed
td
button.jsoneditor-button.jsoneditor-dragarea
td
button.jsoneditor-button.jsoneditor-contextmenu-button <-- button we want to disable here
td
table.jsoneditor-values.my-custom-class <-- custom class attached here
I think the custom class should be attached higher in the tree, this way you can't customize the drag and context menu buttons. With some JavaScript and css selectors it's possible to locate the buttons. It's ugly but at least it's a working workaround:
https://jsbin.com/fidoqok/edit?html,output
<!DOCTYPE HTML>
<html>
<head>
<title>JSONEditor | Disable context menu button</title>
<link href="https://unpkg.com/[email protected]/dist/jsoneditor.min.css" rel="stylesheet" type="text/css">
<script src="https://unpkg.com/[email protected]/dist/jsoneditor.min.js"></script>
<script src="https://unpkg.com/[email protected]/lodash.js"></script>
</head>
<body>
<p>
Context menu of property "disabled" is disabled
</p>
<div id="jsoneditor"></div>
<script>
const disabledClassName = 'disabled-context-menu'
const container = document.getElementById('jsoneditor')
const disableContextMenus = _.debounce(() => {
const items = container.querySelectorAll('.' + disabledClassName)
items.forEach(item => {
// locate the context menu button
const root = item.parentNode.parentNode
const button = root.querySelector('.jsoneditor-contextmenu-button')
button.style.pointerEvents = 'none'
})
})
const options = {
onClassName: ({ path }) => {
if (path && path[0] === 'disabled') {
disableContextMenus()
return disabledClassName
}
return undefined
}
}
const editor = new JSONEditor(container, options)
const json = {
'enabled': [1, 2, 3],
'disabled': [1, 2, 3]
}
editor.set(json)
</script>
</body>
</html>
Forgot to get back to this, but yes, I did manage to make it work using a similar dive and surface algorithm as yours and a utility callback. My algorithms for dynamic parsing of two-layer editability was already quite a messy one, so it wasn't a big deal to add another element to it. Cheers!
Thanks for sharing your solution Alberto 👍
I keep this issue open to change the place where the custom class name is added in the DOM. Will Change the title accordingly.
There's no need for the (visible) debounce delay if you initially set all context menu buttons to display:none and then later set all buttons to display:block when there's no custom class 'disabled-context-menu'.
is there a way I can add custom styles instead of creating a class?
no