From f3459430b00021d6f5131367ceec6db20c51f61c Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Wed, 15 Jul 2020 10:24:39 +0200 Subject: [PATCH] Unify selection on mousedown --- src/JSONNode.scss | 12 +++++++++--- src/JSONNode.svelte | 40 ++++++++++++++++++++++++++++++++-------- src/styles.scss | 1 + src/utils/domUtils.js | 42 +++++++++++++++++++++++++++++++++++------- 4 files changed, 77 insertions(+), 18 deletions(-) diff --git a/src/JSONNode.scss b/src/JSONNode.scss index 4513996..2d4845f 100644 --- a/src/JSONNode.scss +++ b/src/JSONNode.scss @@ -32,6 +32,8 @@ right: $input-padding; height: $selector-height; box-sizing: border-box; + padding-left: $indentation-width; + z-index: 1; .selector { margin-top: $selector-height / 2; @@ -39,13 +41,13 @@ &:hover { .selector { - border: 1px dashed $selection-background; + border: 1px dashed $theme-color; } } &.selected { .selector { - border: 1px dashed $theme-color; + border: 1px dashed $gray; } } } @@ -54,8 +56,12 @@ top: -$selector-height/2 - 1px; } - .after-node-selector { + .append-node-selector { bottom: -$selector-height/2 - 1px; + + .selector { + margin-top: $selector-height / 2 - 2px; + } } .header { diff --git a/src/JSONNode.svelte b/src/JSONNode.svelte index 5c22d63..932b247 100644 --- a/src/JSONNode.svelte +++ b/src/JSONNode.svelte @@ -14,7 +14,10 @@ import { singleton } from './singleton.js' import { getPlainText, - isChildOfButton, isContentEditableDiv, + isAppendNodeSelector, + isBeforeNodeSelector, + isChildOfButton, + isContentEditableDiv, setPlainText } from './utils/domUtils.js' import Icon from 'svelte-awesome' @@ -218,7 +221,27 @@ } // check if the mouse down is not happening in the key or value input fields or on a button - if (!isContentEditableDiv(event.target) && !isChildOfButton(event.target)) { + if (isContentEditableDiv(event.target) || isChildOfButton(event.target)) { + return + } + + if (isBeforeNodeSelector(event.target)) { + singleton.mousedown = true + singleton.selectionAnchor = path + singleton.selectionFocus = null + + onSelect({ + beforePath: path + }) + } else if (isAppendNodeSelector(event.target)) { + singleton.mousedown = true + singleton.selectionAnchor = path + singleton.selectionFocus = null + + onSelect({ + appendPath: path + }) + } else { // initialize dragging a selection singleton.mousedown = true singleton.selectionAnchor = path @@ -228,17 +251,18 @@ anchorPath: singleton.selectionAnchor, focusPath: singleton.selectionFocus }) - - event.stopPropagation() } + event.stopPropagation() + event.preventDefault() + // we attache the mouse up event listener to the global document, // so we will not miss if the mouse up is happening outside of the editor document.addEventListener('mouseup', handleMouseUp) } function handleMouseMove (event) { - if (singleton.mousedown) { + if (singleton.mousedown) { // TODO: Remove the need for this, only create mousemove handle when mouse is down event.preventDefault() event.stopPropagation() @@ -314,10 +338,10 @@ on:mousemove={handleMouseMove} >
@@ -372,10 +396,10 @@ /> {/each}
@@ -440,10 +464,10 @@ /> {/each}
diff --git a/src/styles.scss b/src/styles.scss index 8108918..c51c41a 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -21,6 +21,7 @@ $highlight-active-hover-color: #f3cd00; $warning-color: #FBB917; $error-background-color: #ffef8b; $error-border-color: #ffd700; +$dark-gray: #656565; $gray: #9d9d9d; $gray-icon: $gray; $light-gray: #c0c0c0; diff --git a/src/utils/domUtils.js b/src/utils/domUtils.js index abd1760..69fe58a 100644 --- a/src/utils/domUtils.js +++ b/src/utils/domUtils.js @@ -181,16 +181,44 @@ export function traverseInnerText (element, buffer) { // test whether a DOM element is a child of a button export function isChildOfButton (element) { - let e = element - - while (e && e.nodeName !== 'BUTTON') { - e = e.parentNode - } - - return e && e.nodeName === 'BUTTON' + return isChildOf(element, e => e.nodeName === 'BUTTON') } // test whether a DOM element is a content editable div export function isContentEditableDiv (element) { return (element.nodeName === 'DIV' && element.contentEditable === 'true') } + +export function isBeforeNodeSelector (element) { + return isChildOf(element, e => { + return hasAttribute(e, 'data-type', 'before-node-selector') + }) +} + +export function isAppendNodeSelector (element) { + return isChildOf(element, e => { + return hasAttribute(e, 'data-type', 'append-node-selector') + }) +} + +function hasAttribute(element, name, value) { + return typeof element.getAttribute === 'function' && element.getAttribute(name) === value +} + +/** + * Test if the element or one of it's parents has a certain predicate + * Can be use for example to check whether the element or it's parent has + * a specific attribute or nodeName. + * @param {HTMLElement} element + * @param {function (element: HTMLElement) : boolean} predicate + * @returns {*} + */ +export function isChildOf (element, predicate) { + let e = element + + while (e && !predicate(e)) { + e = e.parentNode + } + + return e && predicate(e) +}