diff --git a/src/JSONEditor.svelte b/src/JSONEditor.svelte
index ee9609e..4065493 100644
--- a/src/JSONEditor.svelte
+++ b/src/JSONEditor.svelte
@@ -32,9 +32,9 @@
} from './utils/immutabilityHelpers.js'
import { compileJSONPointer, parseJSONPointer } from './utils/jsonPointer.js'
import { keyComboFromEvent } from './utils/keyBindings.js'
- import { flattenSearch, search } from './utils/search.js'
+ import { search, searchNext, searchPrevious } from './utils/search.js'
import { immutableJSONPatch } from './utils/immutableJSONPatch'
- import { isEqual, isNumber, initial, last, cloneDeep, first } from 'lodash-es'
+ import { isNumber, initial, last, cloneDeep } from 'lodash-es'
import jump from './assets/jump.js/src/jump.js'
import { syncState } from './utils/syncState.js'
import { getNextKeys, patchProps } from './utils/updateProps.js'
@@ -42,12 +42,13 @@
let divContents
let domHiddenInput
- export let doc = {}
- let state = undefined
- let selection = null
-
export let onChangeJson = () => {}
+ export let doc = {}
+ let state = undefined
+ let searchResult = undefined
+
+ let selection = null
let clipboard = null
$: hasSelectionContents = selection != null && selection.paths != null
$: hasClipboardContents = clipboard != null && selection != null
@@ -78,6 +79,7 @@
export function set(newDocument) {
doc = newDocument
+ searchResult = undefined
state = undefined
history.clear()
}
@@ -98,6 +100,7 @@
doc = documentPatchResult.json
state = patchProps(statePatchResult.json, operations)
+ searchResult = search(doc, searchText, searchResult)
if (newSelection) {
selection = newSelection
}
@@ -222,6 +225,7 @@
if (item) {
doc = immutableJSONPatch(doc, item.undo).json
state = item.prevState
+ searchResult = search(doc, searchText, searchResult)
selection = item.prevSelection
console.log('undo', { item, doc, state, selection })
@@ -237,6 +241,7 @@
if (item) {
doc = immutableJSONPatch(doc, item.redo).json
state = item.state
+ searchResult = search(doc, searchText, searchResult)
selection = item.selection
console.log('redo', { item, doc, state, selection })
@@ -246,49 +251,28 @@
}
}
- // TODO: refactor the search solution and move it in a separate component
- // in: doc, searchText, activeSearchResultIndex
- // out: searchResultWithActive
- // callbacks: change searchText, change doc, change activeSearchResultIndex
- let searchResult
- let activeSearchResult = undefined
- let activeSearchResultIndex
- let flatSearchResult
- let searchResultWithActive
- $: searchResult = searchText ? search(doc, searchText) : undefined
- $: flatSearchResult = flattenSearch(searchResult)
-
- $: {
- if (!activeSearchResult || !existsIn(searchResult, activeSearchResult.path.concat(activeSearchResult.what))) {
- activeSearchResult = flatSearchResult[0]
- focusActiveSearchResult()
- }
+ function changeSearchText (text) {
+ searchText = text
+ searchResult = search(doc, searchText, searchResult)
}
- $: 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] || first(flatSearchResult)
- focusActiveSearchResult()
+ searchResult = searchNext(searchResult)
+ focusActiveSearchResult(searchResult && searchResult.activeItem)
}
function previousSearchResult () {
- activeSearchResult = flatSearchResult[activeSearchResultIndex - 1] || last(flatSearchResult)
- focusActiveSearchResult()
+ searchResult = searchPrevious(searchResult)
+ focusActiveSearchResult(searchResult && searchResult.activeItem)
}
- async function focusActiveSearchResult () {
- if (activeSearchResult) {
- expandPath(activeSearchResult.path)
+ async function focusActiveSearchResult (activeItem) {
+ if (activeItem) {
+ expandPath(activeItem.path)
await tick()
- scrollTo(activeSearchResult.path.concat(activeSearchResult.what))
+ scrollTo(activeItem.path.concat(activeItem.what))
}
}
@@ -549,9 +533,9 @@
searchText = text}
+ resultCount={searchResult ? searchResult.count : 0}
+ activeIndex={searchResult ? searchResult.activeIndex : 0}
+ onChange={changeSearchText}
onNext={nextSearchResult}
onPrevious={previousSearchResult}
onClose={() => {
@@ -574,7 +558,7 @@
value={doc}
path={[]}
state={state}
- searchResult={searchResultWithActive}
+ searchResult={searchResult && searchResult.itemsWithActive}
onPatch={handlePatch}
onUpdateKey={handleUpdateKey}
onExpand={handleExpand}
diff --git a/src/types.js b/src/types.js
index 5ed3693..8ebee0f 100644
--- a/src/types.js
+++ b/src/types.js
@@ -54,16 +54,24 @@
* paths: Path[],
* pathsMap: Object
* }} MultiSelection
- *
- * @typedef {{beforePath: Path}} BeforeSelection
- *
- * @typedef {{appendPath: Path}} AppendSelection
+ */
+/**
+ * @typedef {{beforePath: Path}} BeforeSelection
+ */
+
+/**
+ * @typedef {{appendPath: Path}} AppendSelection
+ */
+
+/**
* @typedef {MultiSelection | BeforeSelection | AppendSelection} Selection
*/
/**
* @typedef {{anchorPath: Path, focusPath: Path}} MultiSelectionSchema
- *
+ */
+
+/**
* @typedef {MultiSelectionSchema | BeforeSelection | AppendSelection} SelectionSchema
*/
diff --git a/src/utils/search.js b/src/utils/search.js
index 0462bec..b988d49 100644
--- a/src/utils/search.js
+++ b/src/utils/search.js
@@ -1,9 +1,103 @@
-import { isNumber } from 'lodash-es'
+import { isEqual, isNumber } from 'lodash-es'
import { STATE_SEARCH_PROPERTY, STATE_SEARCH_VALUE } from '../constants.js'
+import { existsIn, setIn } from './immutabilityHelpers.js'
import { valueType } from './typeUtils.js'
-export function search (doc, searchText) {
- return searchRecursive(null, doc, searchText)
+
+/**
+ * @typedef {{path: Path, what: Symbol}} SearchItem
+ */
+
+/**
+ * @typedef {Object} SearchResult
+ * @property {Object} items
+ * @property {Object} itemsWithActive
+ * @property {SearchItem[]} flatItems
+ * @property {SearchItem} activeItem
+ * @property {number} activeIndex
+ * @property {number} count
+ */
+
+/**
+ * @param {JSON} doc
+ * @param {string} searchText
+ * @param {SearchResult} [previousResult]
+ * @returns {SearchResult}
+ */
+export function search (doc, searchText, previousResult) {
+ if (!searchText || searchText === '') {
+ return undefined
+ }
+
+ const items = searchRecursive(null, doc, searchText)
+
+ const flatItems = flattenSearch(items)
+
+ const activeItem = (previousResult && previousResult.activeItem &&
+ existsIn(items, previousResult.activeItem.path.concat(previousResult.activeItem.what)))
+ ? previousResult.activeItem
+ : flatItems[0]
+
+ const activeIndex = flatItems.findIndex(item => isEqual(item, activeItem))
+
+ const itemsWithActive = (items && activeItem)
+ ? setIn(items, activeItem.path.concat(activeItem.what), 'search active')
+ : items
+
+ return {
+ items,
+ itemsWithActive,
+ flatItems,
+ count: flatItems.length,
+ activeItem,
+ activeIndex
+ }
+}
+
+/**
+ * @param {SearchResult} searchResult
+ * @return {SearchResult} nextResult
+ */
+export function searchNext (searchResult) {
+ const nextActiveIndex = searchResult.activeIndex < searchResult.flatItems.length - 1
+ ? searchResult.activeIndex + 1
+ : 0
+
+ const nextActiveItem = searchResult.flatItems[nextActiveIndex]
+
+ const itemsWithActive = nextActiveItem
+ ? setIn(searchResult.items, nextActiveItem.path.concat(nextActiveItem.what), 'search active')
+ : searchResult.items
+
+ return {
+ ...searchResult,
+ itemsWithActive,
+ activeItem: nextActiveItem,
+ activeIndex: nextActiveIndex
+ }
+}
+
+/**
+ * @param {SearchResult} searchResult
+ * @return {SearchResult} nextResult
+ */
+export function searchPrevious (searchResult) {
+ const previousActiveIndex = searchResult.activeIndex > 0
+ ? searchResult.activeIndex - 1
+ : searchResult.flatItems.length - 1
+
+ const previousActiveItem = searchResult.flatItems[previousActiveIndex]
+
+ const itemsWithActive = previousActiveItem
+ ? setIn(searchResult.items, previousActiveItem.path.concat(previousActiveItem.what), 'search active')
+ : searchResult.items
+
+ return {
+ ...searchResult,
+ itemsWithActive,
+ activeItem: previousActiveItem,
+ activeIndex: previousActiveIndex
+ }
}
function searchRecursive (key, doc, searchText) {
@@ -37,7 +131,7 @@ function searchRecursive (key, doc, searchText) {
return results
}
-export function flattenSearch (searchResult) {
+function flattenSearch (searchResult) {
const resultArray = []
function _flattenSearch (value, path) {