From aa4b9635922a7b33047e45f21ae53a54bab22489 Mon Sep 17 00:00:00 2001 From: jos Date: Fri, 30 Dec 2016 10:44:57 +0100 Subject: [PATCH] Changed the structure of SearchResult. Highlight active search result --- src/components/JSONNode.js | 29 +++++++++++++++++------------ src/components/TreeMode.js | 11 ++++++++--- src/components/menu/Search.js | 6 ++++-- src/jsonData.js | 30 ++++++++++++++++-------------- src/jsoneditor.less | 12 ++++++++++-- src/types.js | 5 +++++ test/jsonData.test.js | 29 +++++++++++++++-------------- 7 files changed, 75 insertions(+), 47 deletions(-) diff --git a/src/components/JSONNode.js b/src/components/JSONNode.js index 371ea1d..0264237 100644 --- a/src/components/JSONNode.js +++ b/src/components/JSONNode.js @@ -295,7 +295,7 @@ export default class JSONNode extends Component { * @param {string} type * @param {boolean} isUrl * @param {boolean} isEmpty - * @param {boolean} [searchValue] + * @param {'normal' | 'active'} [searchValue] * @return {string} * @public */ @@ -304,7 +304,9 @@ export default class JSONNode extends Component { 'jsoneditor-' + type + (isUrl ? ' jsoneditor-url' : '') + (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) { - // TODO: focus to input field - // if (this.props.data.focusProperty && !prevProps.data.focusProperty) { - // console.log('focus property', this.getPath()) - // this.refs.property.focus() - // } - // - // if (this.props.data.focusValue && !prevProps.data.focusValue) { - // console.log('focus value', this.getPath()) - // this.refs.value.focus() - // } + if (this.props.data.focusProperty && !prevProps.data.focusProperty) { + console.log('focus property', this.getPath()) // TODO: cleanup + if (this.refs.property) { + this.refs.property.focus() + } + } + + if (this.props.data.focusValue && !prevProps.data.focusValue) { + console.log('focus value', this.getPath()) // TODO: cleanup + if (this.refs.value) { + this.refs.value.focus() + } + } } static getRootName (data, options) { diff --git a/src/components/TreeMode.js b/src/components/TreeMode.js index 8731e7e..1ea4ff5 100644 --- a/src/components/TreeMode.js +++ b/src/components/TreeMode.js @@ -115,11 +115,16 @@ export default class TreeMode extends Component { // enrich the data with search results const searchResults = this.state.search.text ? search(data, this.state.search.text) : null 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', { className: `jsoneditor jsoneditor-mode-${props.mode}`, diff --git a/src/components/menu/Search.js b/src/components/menu/Search.js index dd34c32..c3c7a41 100644 --- a/src/components/menu/Search.js +++ b/src/components/menu/Search.js @@ -54,12 +54,14 @@ export default class Search extends Component { return null } - if (resultsCount == 0) { + if (resultsCount === 0) { return h('div', {key: 'count', className: 'jsoneditor-results'}, '(no results)') } 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 diff --git a/src/jsonData.js b/src/jsonData.js index 15f9027..3e7ee89 100644 --- a/src/jsonData.js +++ b/src/jsonData.js @@ -1,3 +1,5 @@ +// @flow weak + /** * This file contains functions to act on a JSONData object. * 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 isEqual from 'lodash/isEqual' +import type {SearchResult} from './types' + /** * Expand function which will expand all nodes * @param {Path} path @@ -509,16 +513,16 @@ export function addErrors (data, errors) { * @param {string} text * @return {SearchResult[]} Returns a list with search results */ -export function search (data, text) { - let results = [] +export function search (data, text): SearchResult[] { + let results: SearchResult[] = [] - traverse(data, function (value, path) { + traverse(data, function (value, path, root) { // search in values if (value.type === 'value') { if (containsCaseInsensitive(value.value, text)) { results.push({ dataPath: path, - value: true + type: 'value' }) } } @@ -529,7 +533,7 @@ export function search (data, text) { if (containsCaseInsensitive(prop.name, text)) { results.push({ dataPath: path.concat(prop.name), - property: true + type: 'property' }) } }) @@ -541,24 +545,22 @@ export function search (data, text) { /** * 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 if (searchResults) { searchResults.forEach(function (searchResult) { - if (searchResult.value) { + if (searchResult.type === 'value') { 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') - updatedData = setIn(updatedData, dataPath, true) + const value = isEqual(searchResult, activeSearchResult) ? 'active' : 'normal' + updatedData = setIn(updatedData, dataPath, value) } }) } diff --git a/src/jsoneditor.less b/src/jsoneditor.less index d74ec3c..fe31ec5 100644 --- a/src/jsoneditor.less +++ b/src/jsoneditor.less @@ -260,8 +260,16 @@ div.jsoneditor-value.jsoneditor-empty::after { background-color: yellow; } -.jsoneditor-highlight-primary { - background-color: #FF9632; +.jsoneditor-highlight:hover { + background-color: #f0f000; +} + +.jsoneditor-highlight-active { + background-color: #ffd700; +} + +.jsoneditor-highlight-active:hover { + background-color: #f3cd00; } .jsoneditor-button-placeholder { diff --git a/src/types.js b/src/types.js index 1bf0226..98a64d6 100644 --- a/src/types.js +++ b/src/types.js @@ -71,6 +71,11 @@ type JSONArrayType = Array; export type Path = string[] +export type SearchResult = { + dataPath: Path, + type: 'value' | 'property' +} + export type SetOptions = { expand?: (path: Path) => boolean } diff --git a/test/jsonData.test.js b/test/jsonData.test.js index c089eea..887dec3 100644 --- a/test/jsonData.test.js +++ b/test/jsonData.test.js @@ -278,7 +278,7 @@ const JSON_DATA_EXAMPLE_SEARCH_L = { value: { type: 'value', value: 4, - searchProperty: true + searchProperty: 'normal' } } ] @@ -294,7 +294,7 @@ const JSON_DATA_EXAMPLE_SEARCH_L = { value: { type: 'value', value: 'hello world', - searchValue: true + searchValue: 'normal' } }, { @@ -302,8 +302,8 @@ const JSON_DATA_EXAMPLE_SEARCH_L = { value: { type: 'value', value: null, - searchProperty: true, - searchValue: true + searchProperty: 'active', + searchValue: 'normal' } }, { @@ -311,8 +311,8 @@ const JSON_DATA_EXAMPLE_SEARCH_L = { value: { type: 'value', value: false, - searchProperty: true, - searchValue: true + searchProperty: 'normal', + searchValue: 'normal' } } ] @@ -925,16 +925,17 @@ test('search', t => { // console.log(searchResults) t.deepEqual(searchResults, [ - {dataPath: ['nill'], property: true}, - {dataPath: ['bool'], property: true}, - {dataPath: ['obj', 'arr', '2', 'last'], property: true}, - {dataPath: ['str'], value: true}, - {dataPath: ['nill'], value: true}, - {dataPath: ['bool'], value: true} + {dataPath: ['nill'], type: 'property'}, + {dataPath: ['bool'], type: 'property'}, + {dataPath: ['obj', 'arr', '2', 'last'], type: 'property'}, + {dataPath: ['str'], type: 'value'}, + {dataPath: ['nill'], type: 'value'}, + {dataPath: ['bool'], type: 'value'} ]) - const updatedData = addSearchResults(JSON_DATA_EXAMPLE, searchResults) - // console.log(JSON.stringify(updatedData, null, 2)) + const activeSearchResult = searchResults[0] + const updatedData = addSearchResults(JSON_DATA_EXAMPLE, searchResults, activeSearchResult) + //console.log(JSON.stringify(updatedData, null, 2)) t.deepEqual(updatedData, JSON_DATA_EXAMPLE_SEARCH_L) })