Move logic of search into `search.js`

This commit is contained in:
Jos de Jong 2020-07-22 16:53:18 +02:00
parent be87b1e4cd
commit 7d67ecc4bc
3 changed files with 137 additions and 51 deletions

View File

@ -32,9 +32,9 @@
} from './utils/immutabilityHelpers.js' } from './utils/immutabilityHelpers.js'
import { compileJSONPointer, parseJSONPointer } from './utils/jsonPointer.js' import { compileJSONPointer, parseJSONPointer } from './utils/jsonPointer.js'
import { keyComboFromEvent } from './utils/keyBindings.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 { 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 jump from './assets/jump.js/src/jump.js'
import { syncState } from './utils/syncState.js' import { syncState } from './utils/syncState.js'
import { getNextKeys, patchProps } from './utils/updateProps.js' import { getNextKeys, patchProps } from './utils/updateProps.js'
@ -42,12 +42,13 @@
let divContents let divContents
let domHiddenInput let domHiddenInput
export let doc = {}
let state = undefined
let selection = null
export let onChangeJson = () => {} export let onChangeJson = () => {}
export let doc = {}
let state = undefined
let searchResult = undefined
let selection = null
let clipboard = null let clipboard = null
$: hasSelectionContents = selection != null && selection.paths != null $: hasSelectionContents = selection != null && selection.paths != null
$: hasClipboardContents = clipboard != null && selection != null $: hasClipboardContents = clipboard != null && selection != null
@ -78,6 +79,7 @@
export function set(newDocument) { export function set(newDocument) {
doc = newDocument doc = newDocument
searchResult = undefined
state = undefined state = undefined
history.clear() history.clear()
} }
@ -98,6 +100,7 @@
doc = documentPatchResult.json doc = documentPatchResult.json
state = patchProps(statePatchResult.json, operations) state = patchProps(statePatchResult.json, operations)
searchResult = search(doc, searchText, searchResult)
if (newSelection) { if (newSelection) {
selection = newSelection selection = newSelection
} }
@ -222,6 +225,7 @@
if (item) { if (item) {
doc = immutableJSONPatch(doc, item.undo).json doc = immutableJSONPatch(doc, item.undo).json
state = item.prevState state = item.prevState
searchResult = search(doc, searchText, searchResult)
selection = item.prevSelection selection = item.prevSelection
console.log('undo', { item, doc, state, selection }) console.log('undo', { item, doc, state, selection })
@ -237,6 +241,7 @@
if (item) { if (item) {
doc = immutableJSONPatch(doc, item.redo).json doc = immutableJSONPatch(doc, item.redo).json
state = item.state state = item.state
searchResult = search(doc, searchText, searchResult)
selection = item.selection selection = item.selection
console.log('redo', { item, doc, state, selection }) console.log('redo', { item, doc, state, selection })
@ -246,49 +251,28 @@
} }
} }
// TODO: refactor the search solution and move it in a separate component function changeSearchText (text) {
// in: doc, searchText, activeSearchResultIndex searchText = text
// out: searchResultWithActive searchResult = search(doc, searchText, searchResult)
// 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()
} }
}
$: activeSearchResultIndex = flatSearchResult.findIndex(item => isEqual(item, activeSearchResult))
$: searchResultWithActive = searchResult
? activeSearchResult
? setIn(searchResult, activeSearchResult.path.concat(activeSearchResult.what), 'search active')
: searchResult
: undefined
function nextSearchResult () { function nextSearchResult () {
activeSearchResult = flatSearchResult[activeSearchResultIndex + 1] || first(flatSearchResult) searchResult = searchNext(searchResult)
focusActiveSearchResult() focusActiveSearchResult(searchResult && searchResult.activeItem)
} }
function previousSearchResult () { function previousSearchResult () {
activeSearchResult = flatSearchResult[activeSearchResultIndex - 1] || last(flatSearchResult) searchResult = searchPrevious(searchResult)
focusActiveSearchResult() focusActiveSearchResult(searchResult && searchResult.activeItem)
} }
async function focusActiveSearchResult () { async function focusActiveSearchResult (activeItem) {
if (activeSearchResult) { if (activeItem) {
expandPath(activeSearchResult.path) expandPath(activeItem.path)
await tick() await tick()
scrollTo(activeSearchResult.path.concat(activeSearchResult.what)) scrollTo(activeItem.path.concat(activeItem.what))
} }
} }
@ -549,9 +533,9 @@
<div class="search-box-container"> <div class="search-box-container">
<SearchBox <SearchBox
text={searchText} text={searchText}
resultCount={flatSearchResult.length} resultCount={searchResult ? searchResult.count : 0}
activeIndex={activeSearchResultIndex} activeIndex={searchResult ? searchResult.activeIndex : 0}
onChange={(text) => searchText = text} onChange={changeSearchText}
onNext={nextSearchResult} onNext={nextSearchResult}
onPrevious={previousSearchResult} onPrevious={previousSearchResult}
onClose={() => { onClose={() => {
@ -574,7 +558,7 @@
value={doc} value={doc}
path={[]} path={[]}
state={state} state={state}
searchResult={searchResultWithActive} searchResult={searchResult && searchResult.itemsWithActive}
onPatch={handlePatch} onPatch={handlePatch}
onUpdateKey={handleUpdateKey} onUpdateKey={handleUpdateKey}
onExpand={handleExpand} onExpand={handleExpand}

View File

@ -54,16 +54,24 @@
* paths: Path[], * paths: Path[],
* pathsMap: Object<string, boolean> * pathsMap: Object<string, boolean>
* }} MultiSelection * }} MultiSelection
* */
* @typedef {{beforePath: Path}} BeforeSelection
*
* @typedef {{appendPath: Path}} AppendSelection
/**
* @typedef {{beforePath: Path}} BeforeSelection
*/
/**
* @typedef {{appendPath: Path}} AppendSelection
*/
/**
* @typedef {MultiSelection | BeforeSelection | AppendSelection} Selection * @typedef {MultiSelection | BeforeSelection | AppendSelection} Selection
*/ */
/** /**
* @typedef {{anchorPath: Path, focusPath: Path}} MultiSelectionSchema * @typedef {{anchorPath: Path, focusPath: Path}} MultiSelectionSchema
* */
/**
* @typedef {MultiSelectionSchema | BeforeSelection | AppendSelection} SelectionSchema * @typedef {MultiSelectionSchema | BeforeSelection | AppendSelection} SelectionSchema
*/ */

View File

@ -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 { STATE_SEARCH_PROPERTY, STATE_SEARCH_VALUE } from '../constants.js'
import { existsIn, setIn } from './immutabilityHelpers.js'
import { valueType } from './typeUtils.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) { function searchRecursive (key, doc, searchText) {
@ -37,7 +131,7 @@ function searchRecursive (key, doc, searchText) {
return results return results
} }
export function flattenSearch (searchResult) { function flattenSearch (searchResult) {
const resultArray = [] const resultArray = []
function _flattenSearch (value, path) { function _flattenSearch (value, path) {