Fixed #723: schema error popup not always fully visible

This commit is contained in:
jos 2019-09-11 15:59:19 +02:00
parent e23eb8f191
commit 83e9e0655c
5 changed files with 146 additions and 92 deletions

View File

@ -5,6 +5,7 @@ https://github.com/josdejong/jsoneditor
## not yet published, version 7.0.4
- Fixed #723: schema error popup not always fully visible.
- Fixed wrong text color in search box when using JSONEditor in combination
with bootstrap. See #791. Thanks @dmitry-kulikov.
- Fixed react examples not working out of the box when cloning or downloading

View File

@ -288,32 +288,49 @@ export class Node {
this.dom.tdValue.parentNode.appendChild(tdError)
}
const popover = document.createElement('div')
popover.className = 'jsoneditor-popover jsoneditor-right'
popover.appendChild(document.createTextNode(error.message))
const button = document.createElement('button')
button.type = 'button'
button.className = 'jsoneditor-button jsoneditor-schema-error'
button.appendChild(popover)
// update the direction of the popover
button.onmouseover = button.onfocus = function updateDirection () {
const directions = ['right', 'above', 'below', 'left']
for (let i = 0; i < directions.length; i++) {
const direction = directions[i]
popover.className = 'jsoneditor-popover jsoneditor-' + direction
const contentRect = this.editor.content.getBoundingClientRect()
const popoverRect = popover.getBoundingClientRect()
const margin = 20 // account for a scroll bar
const fit = insideRect(contentRect, popoverRect, margin)
if (fit) {
break
}
const destroy = () => {
if (this.dom.popupAnchor) {
this.dom.popupAnchor.destroy() // this will trigger the onDestroy callback
}
}.bind(this)
}
const onDestroy = () => {
delete this.dom.popupAnchor
}
const createPopup = (destroyOnMouseOut) => {
const frame = this.editor.frame
this.dom.popupAnchor = createAbsoluteAnchor(button, frame, onDestroy, destroyOnMouseOut)
const popupWidth = 200; // must correspond to what's configured in the CSS
const buttonRect = button.getBoundingClientRect()
const frameRect = frame.getBoundingClientRect()
const position = (frameRect.width - buttonRect.x > (popupWidth / 2 + 20))
? 'jsoneditor-above'
: 'jsoneditor-left'
const popover = document.createElement('div')
popover.className = 'jsoneditor-popover ' + position
popover.appendChild(document.createTextNode(error.message))
this.dom.popupAnchor.appendChild(popover)
}
button.onmouseover = () => {
if (!this.dom.popupAnchor) {
createPopup(true)
}
}
button.onfocus = () => {
destroy()
createPopup(false)
}
button.onblur = () => {
destroy()
}
// when clicking the error icon, expand all nodes towards the invalid
// child node, and set focus to the child node
@ -861,6 +878,11 @@ export class Node {
if (table) {
table.removeChild(tr)
}
if (this.dom.popupAnchor) {
this.dom.popupAnchor.destroy()
}
this.hideChilds(options)
}

View File

@ -5,10 +5,11 @@ import { isChildOf, removeEventListener, addEventListener } from './util'
* element.
* @param {HTMLElement} anchor
* @param {HTMLElement} parent
* @param [onDestroy(function(anchor)] Callback when the anchor is destroyed
* @param {function(HTMLElement)} [onDestroy] Callback when the anchor is destroyed
* @param {boolean} [destroyOnMouseOut=false] If true, anchor will be removed on mouse out
* @returns {HTMLElement}
*/
export function createAbsoluteAnchor (anchor, parent, onDestroy) {
export function createAbsoluteAnchor (anchor, parent, onDestroy, destroyOnMouseOut = false) {
const root = getRootNode(anchor)
const eventListeners = {}
@ -48,17 +49,34 @@ export function createAbsoluteAnchor (anchor, parent, onDestroy) {
}
}
function isOutside (target) {
return (target !== absoluteAnchor) && !isChildOf(target, absoluteAnchor)
}
// create and attach event listeners
const destroyIfOutside = event => {
const target = event.target
if ((target !== absoluteAnchor) && !isChildOf(target, absoluteAnchor)) {
function destroyIfOutside (event) {
if (isOutside(event.target)) {
destroy()
}
}
eventListeners.mousedown = addEventListener(root, 'mousedown', destroyIfOutside)
eventListeners.mousewheel = addEventListener(root, 'mousewheel', destroyIfOutside)
// eventListeners.scroll = addEventListener(root, 'scroll', destroyIfOutside);
if (destroyOnMouseOut) {
let destroyTimer = null
absoluteAnchor.onmouseover = () => {
clearTimeout(destroyTimer)
destroyTimer = null
}
absoluteAnchor.onmouseout = () => {
if (!destroyTimer) {
destroyTimer = setTimeout(destroy, 200)
}
}
}
absoluteAnchor.destroy = destroy

View File

@ -82,6 +82,8 @@ div {
}
div {
&.jsoneditor-anchor {
cursor: pointer;
.picker_wrapper {
&.popup {
&.popup_bottom {
@ -370,12 +372,14 @@ div.jsoneditor-value,
div.jsoneditor td,
div.jsoneditor th,
div.jsoneditor textarea,
div.jsoneditor pre.jsoneditor-preview,
div.jsoneditor .jsoneditor-schema-error {
pre.jsoneditor-preview,
.jsoneditor-schema-error,
.jsoneditor-popover{
font-family: $jse-font-mono;
font-size: $jse-font-size;
color: $jse-content-color;
}
.jsoneditor-schema-error {
cursor: default;
display: inline-block;
@ -384,74 +388,76 @@ div.jsoneditor .jsoneditor-schema-error {
position: relative;
text-align: center;
width: 24px;
.jsoneditor-popover {
background-color: $jse-popover-bg;
border-radius: 3px;
box-shadow: $jse-box-shadow-sm;
color: $jse-white;
display: none;
padding: 7px 10px;
position: absolute;
width: 200px;
z-index: 4;
&.jsoneditor-above {
bottom: 32px;
left: -98px;
&:before {
border-top: 7px solid $jse-popover-bg;
bottom: -7px;
}
}
&.jsoneditor-below {
top: 32px;
left: -98px;
&:before {
border-bottom: 7px solid $jse-popover-bg;
top: -7px;
}
}
&.jsoneditor-left {
top: -7px;
right: 32px;
&:before {
border-left: 7px solid $jse-popover-bg;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
content: "";
top: 19px;
right: -14px;
left: inherit;
margin-left: inherit;
margin-top: -7px;
position: absolute;
}
}
&.jsoneditor-right {
top: -7px;
left: 32px;
&:before {
border-right: 7px solid $jse-popover-bg;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
content: "";
top: 19px;
left: -14px;
margin-left: inherit;
margin-top: -7px;
position: absolute;
}
}
}
.jsoneditor-popover {
background-color: $jse-popover-bg;
border-radius: 3px;
box-shadow: $jse-box-shadow-sm;
color: $jse-white;
padding: 7px 10px;
position: absolute;
cursor: auto;
width: 200px;
z-index: 999;
&.jsoneditor-above {
bottom: 32px;
left: -98px;
&:before {
border-right: 7px solid transparent;
border-left: 7px solid transparent;
border-top: 7px solid $jse-popover-bg;
bottom: -7px;
}
}
&.jsoneditor-below {
top: 32px;
left: -98px;
&:before {
border-bottom: 7px solid $jse-popover-bg;
top: -7px;
}
}
&.jsoneditor-left {
top: -7px;
right: 32px;
&:before {
border-left: 7px solid $jse-popover-bg;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
content: "";
display: block;
left: 50%;
margin-left: -7px;
top: 19px;
right: -14px;
left: inherit;
margin-left: inherit;
margin-top: -7px;
position: absolute;
}
}
&.jsoneditor-right {
top: -7px;
left: 32px;
&:before {
border-right: 7px solid $jse-popover-bg;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
content: "";
top: 19px;
left: -14px;
margin-left: inherit;
margin-top: -7px;
position: absolute;
}
}
&:before {
border-right: 7px solid transparent;
border-left: 7px solid transparent;
content: "";
display: block;
left: 50%;
margin-left: -7px;
position: absolute;
}
}
.jsoneditor-text-errors {
tr {
&.jump-to-line {

View File

@ -15,12 +15,19 @@
padding-left: 40px;
}
html, body {
width: 100%;
padding: 0;
box-sizing: border-box;
}
code {
background-color: #f5f5f5;
}
#jsoneditor {
width: 600px;
max-width: 600px;
width: 90%;
height: 500px;
}
</style>