Skip to content

Commit

Permalink
feat: auto scroll up/down when dragging to select multiple nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
josdejong committed Aug 4, 2021
1 parent 1dcbc41 commit 9e96957
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 8 deletions.
77 changes: 77 additions & 0 deletions src/lib/components/controls/createAutoScrollHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import createDebug from 'debug'
import {
AUTO_SCROLL_INTERVAL,
AUTO_SCROLL_SPEED_FAST,
AUTO_SCROLL_SPEED_NORMAL,
AUTO_SCROLL_SPEED_SLOW
} from '../../constants.js'

const debug = createDebug('jsoneditor:AutoScrollHandler')

export function createAutoScrollHandler(scrollableElement) {
debug('createAutoScrollHandler', scrollableElement)

let autoScrollSpeed // pixels per second
let autoScrollTimer

function calculateSpeed(diff) {
return diff < 20
? AUTO_SCROLL_SPEED_SLOW
: diff < 50
? AUTO_SCROLL_SPEED_NORMAL
: AUTO_SCROLL_SPEED_FAST
}

function autoScrollCallback() {
// debug('auto scroll...')
const diff = autoScrollSpeed * (AUTO_SCROLL_INTERVAL / 1000)

scrollableElement.scrollTop += diff
}

function startAutoScroll(speed) {
if (!autoScrollTimer || speed !== autoScrollSpeed) {
stopAutoScroll()

debug('startAutoScroll', speed)
autoScrollSpeed = speed
autoScrollTimer = setInterval(autoScrollCallback, AUTO_SCROLL_INTERVAL)
}
}

function stopAutoScroll() {
if (autoScrollTimer) {
debug('stopAutoScroll')

clearInterval(autoScrollTimer)
autoScrollTimer = undefined
autoScrollSpeed = undefined
}
}

function onDrag(event) {
if (scrollableElement) {
const y = event.clientY
const { top, bottom } = scrollableElement.getBoundingClientRect()

if (y < top) {
const speed = calculateSpeed(top - y)
startAutoScroll(-speed)
} else if (y > bottom) {
const speed = calculateSpeed(y - bottom)
startAutoScroll(speed)
} else {
stopAutoScroll()
}
}
}

function onDragEnd() {
stopAutoScroll()
}

return {
onDrag,
onDragEnd
}
}
21 changes: 15 additions & 6 deletions src/lib/components/modes/treemode/JSONNode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<script>
import { faCaretDown, faCaretRight } from '@fortawesome/free-solid-svg-icons'
import classnames from 'classnames'
import { compileJSONPointer, getIn, parseJSONPointer } from 'immutable-json-patch'
import { compileJSONPointer, parseJSONPointer } from 'immutable-json-patch'
import { isEqual, last } from 'lodash-es'
import Icon from 'svelte-awesome'
import {
Expand Down Expand Up @@ -49,6 +49,8 @@
export let onPasteJson
export let onContextMenu
export let onClassName
export let onDrag
export let onDragEnd
/** @type {function (path: Path, section: Section)} */
export let onExpandSection
Expand Down Expand Up @@ -176,9 +178,10 @@
}
}
// we attach 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)
// we attach the mousemove and mouseup event listeners to the global document,
// so we will not miss if the mouse events happen outside of the editor
document.addEventListener('mousemove', onDrag, true)
document.addEventListener('mouseup', handleMouseUpGlobal)
}
function handleMouseMove(event) {
Expand Down Expand Up @@ -210,14 +213,16 @@
}
}
function handleMouseUp(event) {
function handleMouseUpGlobal(event) {
if (singleton.mousedown) {
event.stopPropagation()
singleton.mousedown = false
}
document.removeEventListener('mouseup', handleMouseUp)
onDragEnd()
document.removeEventListener('mousemove', onDrag, true)
document.removeEventListener('mouseup', handleMouseUpGlobal)
}
function handleMouseOver(event) {
Expand Down Expand Up @@ -367,6 +372,8 @@
{onExpandSection}
{onContextMenu}
{onClassName}
{onDrag}
{onDragEnd}
{selection}
>
<div slot="identifier" class="identifier">
Expand Down Expand Up @@ -486,6 +493,8 @@
{onExpandSection}
{onContextMenu}
{onClassName}
{onDrag}
{onDragEnd}
{selection}
>
<div slot="identifier" class="identifier">
Expand Down
7 changes: 6 additions & 1 deletion src/lib/components/modes/treemode/TreeMode.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<svelte:options immutable={true} />

<script>
import { createAutoScrollHandler } from '../../../components/controls/createAutoScrollHandler.js'
import { faCheck, faCode, faWrench } from '@fortawesome/free-solid-svg-icons'
import createDebug from 'debug'
import {
Expand Down Expand Up @@ -1577,6 +1578,8 @@
refHiddenInput.focus()
refHiddenInput.select()
}
$: autoScrollHandler = createAutoScrollHandler(refContents)
</script>

<div
Expand Down Expand Up @@ -1637,7 +1640,7 @@
</div>
{/if}
{:else}
<div class="contents" bind:this={refContents}>
<div class="contents" data-jsoneditor-scrollable-contents={true} bind:this={refContents}>
<JSONNode
value={json}
path={[]}
Expand All @@ -1653,6 +1656,8 @@
onExpandSection={handleExpandSection}
onContextMenu={openContextMenu}
onClassName={onClassName || noop}
onDrag={autoScrollHandler.onDrag}
onDragEnd={autoScrollHandler.onDragEnd}
{selection}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<svelte:options immutable={true} />

<script>
import DropdownButton from '$lib/components/controls/DropdownButton.svelte'
import DropdownButton from '../../../../components/controls/DropdownButton.svelte'
import {
faCaretSquareDown,
faCaretSquareUp,
Expand Down
4 changes: 4 additions & 0 deletions src/lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export const SCROLL_DURATION = 300 // ms
export const DEBOUNCE_DELAY = 300
export const SEARCH_UPDATE_THROTTLE = 300 // ms
export const CHECK_VALID_JSON_DELAY = 300 // ms
export const AUTO_SCROLL_INTERVAL = 50 // ms
export const AUTO_SCROLL_SPEED_SLOW = 200 // pixels per second
export const AUTO_SCROLL_SPEED_NORMAL = 400 // pixels per second
export const AUTO_SCROLL_SPEED_FAST = 1200 // pixels per second
export const MAX_SEARCH_RESULTS = 1000
export const ARRAY_SECTION_SIZE = 100
export const DEFAULT_VISIBLE_SECTIONS = [{ start: 0, end: ARRAY_SECTION_SIZE }]
Expand Down
9 changes: 9 additions & 0 deletions src/routes/development/DevelopmentApp.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts">
import { createAjvValidator, JSONEditor } from '$lib'
import { useLocalStorage } from '$lib/utils/localStorageUtils.js'
import { range } from 'lodash-es'
let json = {
array: [1, 2, [3, 4, 5]],
Expand Down Expand Up @@ -133,6 +134,14 @@
>
Set empty string
</button>
<button
on:click={() => {
text = undefined
json = range(0, 999)
}}
>
Set long array
</button>
<button
on:click={() => {
text = 'abc'
Expand Down
1 change: 1 addition & 0 deletions src/routes/index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import DevelopmentApp from './development/DevelopmentApp.svelte'
export const prerender = true
export const ssr = true
</script>

<svelte:head>
Expand Down

0 comments on commit 9e96957

Please sign in to comment.