Changed the structure of SearchResult. Highlight active search result

This commit is contained in:
jos 2016-12-30 10:44:57 +01:00
parent 95f0a31731
commit aa4b963592
7 changed files with 75 additions and 47 deletions

View File

@ -295,7 +295,7 @@ export default class JSONNode extends Component {
* @param {string} type * @param {string} type
* @param {boolean} isUrl * @param {boolean} isUrl
* @param {boolean} isEmpty * @param {boolean} isEmpty
* @param {boolean} [searchValue] * @param {'normal' | 'active'} [searchValue]
* @return {string} * @return {string}
* @public * @public
*/ */
@ -304,7 +304,9 @@ export default class JSONNode extends Component {
'jsoneditor-' + type + 'jsoneditor-' + type +
(isUrl ? ' jsoneditor-url' : '') + (isUrl ? ' jsoneditor-url' : '') +
(isEmpty ? ' jsoneditor-empty' : '') + (isEmpty ? ' jsoneditor-empty' : '') +
(searchValue ? ' jsoneditor-highlight' : '') (searchValue === 'active'
? ' jsoneditor-highlight-active'
: (searchValue ? ' jsoneditor-highlight' : ''))
} }
/** /**
@ -372,16 +374,19 @@ export default class JSONNode extends Component {
} }
componentDidUpdate (prevProps, prevState) { componentDidUpdate (prevProps, prevState) {
// TODO: focus to input field if (this.props.data.focusProperty && !prevProps.data.focusProperty) {
// if (this.props.data.focusProperty && !prevProps.data.focusProperty) { console.log('focus property', this.getPath()) // TODO: cleanup
// console.log('focus property', this.getPath()) if (this.refs.property) {
// this.refs.property.focus() this.refs.property.focus()
// } }
// }
// if (this.props.data.focusValue && !prevProps.data.focusValue) {
// console.log('focus value', this.getPath()) if (this.props.data.focusValue && !prevProps.data.focusValue) {
// this.refs.value.focus() console.log('focus value', this.getPath()) // TODO: cleanup
// } if (this.refs.value) {
this.refs.value.focus()
}
}
} }
static getRootName (data, options) { static getRootName (data, options) {

View File

@ -115,11 +115,16 @@ export default class TreeMode extends Component {
// enrich the data with search results // enrich the data with search results
const searchResults = this.state.search.text ? search(data, this.state.search.text) : null const searchResults = this.state.search.text ? search(data, this.state.search.text) : null
if (searchResults && searchResults.length > 0) { if (searchResults && searchResults.length > 0) {
data = addSearchResults(data, searchResults) const activeSearchResult = searchResults[0] // TODO: store active search result in state
data = addFocus(data, searchResults[0]) // TODO: change to using focus from state data = addSearchResults(data, searchResults, activeSearchResult)
// TODO: highlight
// data = addFocus(data, searchResults[0]) // TODO: change to using focus from state
} }
// TODO: pass number of search results to search box in top menu
console.log('data', data)
return h('div', { return h('div', {
className: `jsoneditor jsoneditor-mode-${props.mode}`, className: `jsoneditor jsoneditor-mode-${props.mode}`,

View File

@ -54,12 +54,14 @@ export default class Search extends Component {
return null return null
} }
if (resultsCount == 0) { if (resultsCount === 0) {
return h('div', {key: 'count', className: 'jsoneditor-results'}, '(no results)') return h('div', {key: 'count', className: 'jsoneditor-results'}, '(no results)')
} }
if (resultsCount > 0) { if (resultsCount > 0) {
return h('div', {key: 'count', className: 'jsoneditor-results'}, this.props.resultsCount + ' results') const suffix = resultsCount === 1 ? ' result' : ' results'
return h('div', {key: 'count', className: 'jsoneditor-results'}, this.props.resultsCount + suffix)
} }
return null return null

View File

@ -1,3 +1,5 @@
// @flow weak
/** /**
* This file contains functions to act on a JSONData object. * This file contains functions to act on a JSONData object.
* All functions are pure and don't mutate the JSONData. * All functions are pure and don't mutate the JSONData.
@ -7,6 +9,8 @@ import { setIn, updateIn, getIn, deleteIn, insertAt } from './utils/immutability
import { isObject } from './utils/typeUtils' import { isObject } from './utils/typeUtils'
import isEqual from 'lodash/isEqual' import isEqual from 'lodash/isEqual'
import type {SearchResult} from './types'
/** /**
* Expand function which will expand all nodes * Expand function which will expand all nodes
* @param {Path} path * @param {Path} path
@ -509,16 +513,16 @@ export function addErrors (data, errors) {
* @param {string} text * @param {string} text
* @return {SearchResult[]} Returns a list with search results * @return {SearchResult[]} Returns a list with search results
*/ */
export function search (data, text) { export function search (data, text): SearchResult[] {
let results = [] let results: SearchResult[] = []
traverse(data, function (value, path) { traverse(data, function (value, path, root) {
// search in values // search in values
if (value.type === 'value') { if (value.type === 'value') {
if (containsCaseInsensitive(value.value, text)) { if (containsCaseInsensitive(value.value, text)) {
results.push({ results.push({
dataPath: path, dataPath: path,
value: true type: 'value'
}) })
} }
} }
@ -529,7 +533,7 @@ export function search (data, text) {
if (containsCaseInsensitive(prop.name, text)) { if (containsCaseInsensitive(prop.name, text)) {
results.push({ results.push({
dataPath: path.concat(prop.name), dataPath: path.concat(prop.name),
property: true type: 'property'
}) })
} }
}) })
@ -541,24 +545,22 @@ export function search (data, text) {
/** /**
* Merge searchResults into the data * Merge searchResults into the data
*
* @param {JSONData} data
* @param {SearchResult[]} searchResults
* @return {JSONData} Returns an updated copy of data
*/ */
export function addSearchResults (data, searchResults) { export function addSearchResults (data, searchResults: SearchResult[], activeSearchResult: SearchResult) {
let updatedData = data let updatedData = data
if (searchResults) { if (searchResults) {
searchResults.forEach(function (searchResult) { searchResults.forEach(function (searchResult) {
if (searchResult.value) { if (searchResult.type === 'value') {
const dataPath = toDataPath(data, searchResult.dataPath).concat('searchValue') const dataPath = toDataPath(data, searchResult.dataPath).concat('searchValue')
updatedData = setIn(updatedData, dataPath, true) const value = isEqual(searchResult, activeSearchResult) ? 'active' : 'normal'
updatedData = setIn(updatedData, dataPath, value)
} }
if (searchResult.property) { if (searchResult.type === 'property') {
const dataPath = toDataPath(data, searchResult.dataPath).concat('searchProperty') const dataPath = toDataPath(data, searchResult.dataPath).concat('searchProperty')
updatedData = setIn(updatedData, dataPath, true) const value = isEqual(searchResult, activeSearchResult) ? 'active' : 'normal'
updatedData = setIn(updatedData, dataPath, value)
} }
}) })
} }

View File

@ -260,8 +260,16 @@ div.jsoneditor-value.jsoneditor-empty::after {
background-color: yellow; background-color: yellow;
} }
.jsoneditor-highlight-primary { .jsoneditor-highlight:hover {
background-color: #FF9632; background-color: #f0f000;
}
.jsoneditor-highlight-active {
background-color: #ffd700;
}
.jsoneditor-highlight-active:hover {
background-color: #f3cd00;
} }
.jsoneditor-button-placeholder { .jsoneditor-button-placeholder {

View File

@ -71,6 +71,11 @@ type JSONArrayType = Array<JSON>;
export type Path = string[] export type Path = string[]
export type SearchResult = {
dataPath: Path,
type: 'value' | 'property'
}
export type SetOptions = { export type SetOptions = {
expand?: (path: Path) => boolean expand?: (path: Path) => boolean
} }

View File

@ -278,7 +278,7 @@ const JSON_DATA_EXAMPLE_SEARCH_L = {
value: { value: {
type: 'value', type: 'value',
value: 4, value: 4,
searchProperty: true searchProperty: 'normal'
} }
} }
] ]
@ -294,7 +294,7 @@ const JSON_DATA_EXAMPLE_SEARCH_L = {
value: { value: {
type: 'value', type: 'value',
value: 'hello world', value: 'hello world',
searchValue: true searchValue: 'normal'
} }
}, },
{ {
@ -302,8 +302,8 @@ const JSON_DATA_EXAMPLE_SEARCH_L = {
value: { value: {
type: 'value', type: 'value',
value: null, value: null,
searchProperty: true, searchProperty: 'active',
searchValue: true searchValue: 'normal'
} }
}, },
{ {
@ -311,8 +311,8 @@ const JSON_DATA_EXAMPLE_SEARCH_L = {
value: { value: {
type: 'value', type: 'value',
value: false, value: false,
searchProperty: true, searchProperty: 'normal',
searchValue: true searchValue: 'normal'
} }
} }
] ]
@ -925,16 +925,17 @@ test('search', t => {
// console.log(searchResults) // console.log(searchResults)
t.deepEqual(searchResults, [ t.deepEqual(searchResults, [
{dataPath: ['nill'], property: true}, {dataPath: ['nill'], type: 'property'},
{dataPath: ['bool'], property: true}, {dataPath: ['bool'], type: 'property'},
{dataPath: ['obj', 'arr', '2', 'last'], property: true}, {dataPath: ['obj', 'arr', '2', 'last'], type: 'property'},
{dataPath: ['str'], value: true}, {dataPath: ['str'], type: 'value'},
{dataPath: ['nill'], value: true}, {dataPath: ['nill'], type: 'value'},
{dataPath: ['bool'], value: true} {dataPath: ['bool'], type: 'value'}
]) ])
const updatedData = addSearchResults(JSON_DATA_EXAMPLE, searchResults) const activeSearchResult = searchResults[0]
// console.log(JSON.stringify(updatedData, null, 2)) const updatedData = addSearchResults(JSON_DATA_EXAMPLE, searchResults, activeSearchResult)
//console.log(JSON.stringify(updatedData, null, 2))
t.deepEqual(updatedData, JSON_DATA_EXAMPLE_SEARCH_L) t.deepEqual(updatedData, JSON_DATA_EXAMPLE_SEARCH_L)
}) })