Move logic of search into `search.js`
This commit is contained in:
parent
be87b1e4cd
commit
7d67ecc4bc
|
@ -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}
|
||||||
|
|
18
src/types.js
18
src/types.js
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue