diff --git a/src/JSONEditor.scss b/src/JSONEditor.scss
index 23a53b4..c28de79 100644
--- a/src/JSONEditor.scss
+++ b/src/JSONEditor.scss
@@ -15,6 +15,7 @@
color: $white;
display: flex;
align-items: center;
+ position: relative;
.button {
width: 32px;
@@ -47,34 +48,18 @@
margin: 5px;
}
- .search-box {
- display: flex;
- align-items: center;
-
- .search-icon {
- margin-right: $menu-padding;
- }
-
- .search-input {
- border: none;
- font-family: $font-family-menu;
- font-size: $font-size;
- padding: $input-padding;
- margin-right: 2px;
- }
+ .search-box-container {
+ position: absolute;
+ top: 100%;
+ right: $search-box-offset + 20px; // keep space for scrollbar
+ margin-top: $search-box-offset;
+ z-index: 1;
}
}
.contents {
flex: 1;
overflow: auto;
- position: relative;
-
- .search {
- position: absolute;
- top: $search-box-offset;
- right: $search-box-offset;
- }
.bottom {
height: $input-padding;
diff --git a/src/JSONEditor.svelte b/src/JSONEditor.svelte
index 2bd5272..2d871ce 100644
--- a/src/JSONEditor.svelte
+++ b/src/JSONEditor.svelte
@@ -4,9 +4,11 @@
import { faSearch, faUndo, faRedo } from '@fortawesome/free-solid-svg-icons'
import { createHistory } from './history.js'
import Node from './JSONNode.svelte'
+ import { existsIn, setIn } from './utils/immutabilityHelpers.js'
import { keyComboFromEvent } from './utils/keyBindings.js'
- import { search } from './utils/search.js'
+ import { flattenSearch, search } from './utils/search.js'
import { immutableJSONPatch } from './utils/immutableJSONPatch'
+ import { isEqual } from 'lodash'
export let json = {}
export let onChangeJson = () => {
@@ -62,7 +64,35 @@
return result
}
+ // TODO: refactor the search solution, it's too complex. Also, move it in a separate component
+ let searchResult
+ let activeSearchResult = undefined
+ let activeSearchResultIndex
+ let flatSearchResult
+ let searchResultWithActive
$: searchResult = searchText ? doSearch(json, searchText) : undefined
+ $: flatSearchResult = flattenSearch(searchResult)
+
+ $: {
+ if (!activeSearchResult || !existsIn(searchResult, activeSearchResult.path.concat(activeSearchResult.what))) {
+ activeSearchResult = flatSearchResult[0]
+ }
+ }
+
+ $: activeSearchResultIndex = flatSearchResult.findIndex(item => isEqual(item, activeSearchResult))
+ $: searchResultWithActive = searchResult
+ ? activeSearchResult
+ ? setIn(searchResult, activeSearchResult.path.concat(activeSearchResult.what), 'search active')
+ : searchResult
+ : undefined
+
+ function nextSearchResult () {
+ activeSearchResult = flatSearchResult[activeSearchResultIndex + 1] || activeSearchResult
+ }
+
+ function previousSearchResult () {
+ activeSearchResult = flatSearchResult[activeSearchResultIndex - 1] || activeSearchResult
+ }
function handleChangeKey(key, oldKey) {
// console.log('handleChangeKey', { key, oldKey })
@@ -178,26 +208,33 @@
+ {#if showSearch}
+
+ searchText = text}
+ onNext={nextSearchResult}
+ onPrevious={previousSearchResult}
+ onClose={() => {
+ showSearch = false
+ searchText = ''
+ }}
+ />
+
+ {/if}
- {#if showSearch}
-
- searchText = text}
- onClose={() => showSearch = false}
- />
-
- {/if}
diff --git a/src/JSONNode.scss b/src/JSONNode.scss
index 72be6c0..68df794 100644
--- a/src/JSONNode.scss
+++ b/src/JSONNode.scss
@@ -147,4 +147,8 @@ div.empty {
.key.search,
.value.search {
background-color: $highlight-color;
+
+ &.active {
+ background-color: $highlight-active-color;
+ }
}
\ No newline at end of file
diff --git a/src/JSONNode.svelte b/src/JSONNode.svelte
index bb51491..3dda4a5 100644
--- a/src/JSONNode.svelte
+++ b/src/JSONNode.svelte
@@ -80,21 +80,15 @@
function getValueClass (value, searchResult) {
const type = valueType (value)
- return classnames('value', type, {
+ return classnames('value', type, searchResult && searchResult[SEARCH_VALUE], {
url: isUrl(value),
empty: typeof value === 'string' && value.length === 0,
- search: searchResult
- ? !!searchResult[SEARCH_VALUE]
- : false
})
}
function getKeyClass(key, searchResult) {
- return classnames('key', {
- empty: key === '',
- search: searchResult
- ? !!searchResult[SEARCH_PROPERTY]
- : false
+ return classnames('key', searchResult && searchResult[SEARCH_PROPERTY], {
+ empty: key === ''
})
}
diff --git a/src/SearchBox.scss b/src/SearchBox.scss
index da512e8..44b6b85 100644
--- a/src/SearchBox.scss
+++ b/src/SearchBox.scss
@@ -6,13 +6,14 @@ $search-size: 24px;
border: 2px solid $theme-color;
border-radius: $border-radius;
background: $white;
+ box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.24);
.search-form {
display: flex;
align-items: center;
padding: 0 5px;
- .search-icon {
+ button {
color: $light-gray;
display: block;
width: $search-size;
@@ -24,13 +25,18 @@ $search-size: 24px;
margin: 0;
}
- button.search-icon {
+ button {
width: 20px;
cursor: pointer;
&:hover,
&:active {
color: $gray;
+
+ &.search-icon {
+ color: $light-gray;
+ cursor: inherit;
+ }
}
}
diff --git a/src/SearchBox.svelte b/src/SearchBox.svelte
index 55b272f..77e9688 100644
--- a/src/SearchBox.svelte
+++ b/src/SearchBox.svelte
@@ -7,44 +7,47 @@
const DEBOUNCE_DELAY = 300 // milliseconds TODO: make the debounce delay configurable?
export let text = ''
+ let inputText = ''
+ export let resultCount = 0
+ export let activeIndex = 0
export let onChange = () => {}
+ export let onPrevious = () => {}
+ export let onNext = () => {}
export let onClose = () => {}
- let activeResultIndex = 0
- let resultCount = 0
-
$: onChangeDebounced = debounce(onChange, DEBOUNCE_DELAY)
function handleSubmit (event) {
event.preventDefault()
- onChangeDebounced.cancel()
- onChange(text)
+ const pendingChanges = text !== inputText
+ if (pendingChanges) {
+ onChangeDebounced.cancel()
+ onChange(inputText)
+ } else {
+ onNext()
+ }
}
function handleInput (event) {
- text = event.target.value
+ inputText = event.target.value
- onChangeDebounced(text)
+ onChangeDebounced(inputText)
// TODO: fire debounced onChange
}
- function handleClose () {
- onChange('')
- onClose()
- }
-
function handleKeyDown (event) {
const combo = keyComboFromEvent(event)
+
if (combo === 'Ctrl+Enter' || combo === 'Command+Enter') {
event.preventDefault()
- // this.props.onFocusActive() // FIXME
+ // TODO: move focus to the active element
}
if (combo === 'Escape') {
event.preventDefault()
- handleClose()
+ onClose()
}
}
@@ -68,15 +71,15 @@
/>
- {activeResultIndex}/{resultCount}
+ {activeIndex + 1}/{resultCount}
-