Fix many small issues in the UI
This commit is contained in:
parent
7064578b31
commit
e979e0f016
|
@ -27,7 +27,7 @@ export function changeValue (eson, path, value) {
|
||||||
path: compileJSONPointer(path),
|
path: compileJSONPointer(path),
|
||||||
value: value,
|
value: value,
|
||||||
jsoneditor: {
|
jsoneditor: {
|
||||||
type: oldDataValue.type
|
type: oldDataValue[META].type
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
@ -102,14 +102,14 @@ export function duplicate (eson, selection) {
|
||||||
const { maxIndex } = findSelectionIndices(root, rootPath, selection)
|
const { maxIndex } = findSelectionIndices(root, rootPath, selection)
|
||||||
const paths = pathsFromSelection(eson, selection)
|
const paths = pathsFromSelection(eson, selection)
|
||||||
|
|
||||||
if (root.type === 'Array') {
|
if (root[META].type === 'Array') {
|
||||||
return paths.map((path, offset) => ({
|
return paths.map((path, offset) => ({
|
||||||
op: 'copy',
|
op: 'copy',
|
||||||
from: compileJSONPointer(path),
|
from: compileJSONPointer(path),
|
||||||
path: compileJSONPointer(rootPath.concat(maxIndex + offset))
|
path: compileJSONPointer(rootPath.concat(maxIndex + offset))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
else { // object.type === 'Object'
|
else { // root[META].type === 'Object'
|
||||||
const before = root[META].props[maxIndex] || null
|
const before = root[META].props[maxIndex] || null
|
||||||
|
|
||||||
return paths.map(path => {
|
return paths.map(path => {
|
||||||
|
@ -144,7 +144,7 @@ export function insertBefore (eson, path, values) { // TODO: find a better name
|
||||||
const parentPath = initial(path)
|
const parentPath = initial(path)
|
||||||
const parent = getIn(eson, parentPath)
|
const parent = getIn(eson, parentPath)
|
||||||
|
|
||||||
if (parent.type === 'Array') {
|
if (parent[META].type === 'Array') {
|
||||||
const startIndex = parseInt(last(path))
|
const startIndex = parseInt(last(path))
|
||||||
return values.map((entry, offset) => ({
|
return values.map((entry, offset) => ({
|
||||||
op: 'add',
|
op: 'add',
|
||||||
|
@ -155,7 +155,7 @@ export function insertBefore (eson, path, values) { // TODO: find a better name
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
else { // object.type === 'Object'
|
else { // parent[META].type === 'Object'
|
||||||
const before = last(path)
|
const before = last(path)
|
||||||
return values.map(entry => {
|
return values.map(entry => {
|
||||||
const newProp = findUniqueName(entry.name, parent[META].props)
|
const newProp = findUniqueName(entry.name, parent[META].props)
|
||||||
|
@ -189,7 +189,7 @@ export function replace (eson, selection, values) { // TODO: find a better name
|
||||||
const root = getIn(eson, rootPath)
|
const root = getIn(eson, rootPath)
|
||||||
const { minIndex, maxIndex } = findSelectionIndices(root, rootPath, selection)
|
const { minIndex, maxIndex } = findSelectionIndices(root, rootPath, selection)
|
||||||
|
|
||||||
if (root.type === 'Array') {
|
if (root[META].type === 'Array') {
|
||||||
const removeActions = removeAll(pathsFromSelection(eson, selection))
|
const removeActions = removeAll(pathsFromSelection(eson, selection))
|
||||||
const insertActions = values.map((entry, offset) => ({
|
const insertActions = values.map((entry, offset) => ({
|
||||||
op: 'add',
|
op: 'add',
|
||||||
|
@ -202,7 +202,7 @@ export function replace (eson, selection, values) { // TODO: find a better name
|
||||||
|
|
||||||
return removeActions.concat(insertActions)
|
return removeActions.concat(insertActions)
|
||||||
}
|
}
|
||||||
else { // object.type === 'Object'
|
else { // root[META].type === 'Object'
|
||||||
const before = root[META].props[maxIndex] || null
|
const before = root[META].props[maxIndex] || null
|
||||||
|
|
||||||
const removeActions = removeAll(pathsFromSelection(eson, selection))
|
const removeActions = removeAll(pathsFromSelection(eson, selection))
|
||||||
|
@ -241,7 +241,7 @@ export function append (eson, parentPath, type) {
|
||||||
const parent = getIn(eson, parentPath)
|
const parent = getIn(eson, parentPath)
|
||||||
const value = createEntry(type)
|
const value = createEntry(type)
|
||||||
|
|
||||||
if (parent.type === 'Array') {
|
if (parent[META].type === 'Array') {
|
||||||
return [{
|
return [{
|
||||||
op: 'add',
|
op: 'add',
|
||||||
path: compileJSONPointer(parentPath.concat('-')),
|
path: compileJSONPointer(parentPath.concat('-')),
|
||||||
|
@ -251,7 +251,7 @@ export function append (eson, parentPath, type) {
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
else { // object.type === 'Object'
|
else { // parent[META].type === 'Object'
|
||||||
const newProp = findUniqueName('', parent[META].props)
|
const newProp = findUniqueName('', parent[META].props)
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
|
@ -306,11 +306,11 @@ export function sort (eson, path, order = null) {
|
||||||
const compare = order === 'desc' ? compareDesc : compareAsc
|
const compare = order === 'desc' ? compareDesc : compareAsc
|
||||||
const object = getIn(eson, path)
|
const object = getIn(eson, path)
|
||||||
|
|
||||||
if (object.type === 'Array') {
|
if (object[META].type === 'Array') {
|
||||||
const orderedItems = object.slice()
|
const orderedItems = cloneWithSymbols(object)
|
||||||
|
|
||||||
// order the items by value
|
// order the items by value
|
||||||
orderedItems.sort((a, b) => compare(a.value, b.value))
|
orderedItems.sort((a, b) => compare(a[META].value, b[META].value))
|
||||||
|
|
||||||
// when no order is provided, test whether ordering ascending
|
// when no order is provided, test whether ordering ascending
|
||||||
// changed anything. If not, sort descending
|
// changed anything. If not, sort descending
|
||||||
|
@ -318,16 +318,18 @@ export function sort (eson, path, order = null) {
|
||||||
orderedItems.reverse()
|
orderedItems.reverse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: refactor into a set of move actions, so we keep eson state of the items
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
op: 'replace',
|
op: 'replace',
|
||||||
path: compileJSONPointer(path),
|
path: compileJSONPointer(path),
|
||||||
value: orderedItems
|
value: esonToJson(orderedItems)
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
else { // object.type === 'Object'
|
else { // object[META].type === 'Object'
|
||||||
|
|
||||||
// order the properties by key
|
// order the properties by key
|
||||||
const orderedProps = object[META].props.slice().sort((a, b) => compare(a.name, b.name))
|
const orderedProps = object[META].props.slice().sort(compare)
|
||||||
|
|
||||||
// when no order is provided, test whether ordering ascending
|
// when no order is provided, test whether ordering ascending
|
||||||
// changed anything. If not, sort descending
|
// changed anything. If not, sort descending
|
||||||
|
@ -338,12 +340,14 @@ export function sort (eson, path, order = null) {
|
||||||
const orderedObject = cloneWithSymbols(object)
|
const orderedObject = cloneWithSymbols(object)
|
||||||
orderedObject[META] = setIn(object[META], ['props'], orderedProps)
|
orderedObject[META] = setIn(object[META], ['props'], orderedProps)
|
||||||
|
|
||||||
|
// TODO: refactor into a set of move actions, so we keep eson state of the items
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
op: 'replace',
|
op: 'replace',
|
||||||
path: compileJSONPointer(path),
|
path: compileJSONPointer(path),
|
||||||
value: esonToJson(orderedObject),
|
value: esonToJson(orderedObject),
|
||||||
jsoneditor: {
|
jsoneditor: {
|
||||||
order: orderedProps.map(prop => prop.name)
|
order: orderedProps // TODO: order isn't used right now in patchEson.
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,10 +254,11 @@ export default class JSONNode extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: simplify the method renderProperty
|
// TODO: simplify the method renderProperty
|
||||||
renderProperty (prop?: ESONObjectProperty, index?: number, eson: ESON, options: {escapeUnicode: boolean, isPropertyEditable: (Path) => boolean}) {
|
renderProperty (prop?: String, index?: number, eson: ESON, options: {escapeUnicode: boolean, isPropertyEditable: (Path) => boolean}) {
|
||||||
const isIndex = typeof index === 'number'
|
const isIndex = typeof index === 'number'
|
||||||
|
const isProp = typeof prop === 'string'
|
||||||
|
|
||||||
if (!prop && !isIndex) {
|
if (!isProp && !isIndex) {
|
||||||
// root node
|
// root node
|
||||||
const rootName = JSONNode.getRootName(eson, options)
|
const rootName = JSONNode.getRootName(eson, options)
|
||||||
|
|
||||||
|
@ -604,7 +605,7 @@ export default class JSONNode extends PureComponent {
|
||||||
/** @private */
|
/** @private */
|
||||||
handleChangeProperty = (event) => {
|
handleChangeProperty = (event) => {
|
||||||
const parentPath = initial(this.props.eson[META].path)
|
const parentPath = initial(this.props.eson[META].path)
|
||||||
const oldProp = this.props.prop.name
|
const oldProp = this.props.prop
|
||||||
const newProp = unescapeHTML(getInnerText(event.target))
|
const newProp = unescapeHTML(getInnerText(event.target))
|
||||||
|
|
||||||
if (newProp !== oldProp) {
|
if (newProp !== oldProp) {
|
||||||
|
|
|
@ -111,7 +111,7 @@ export default class TreeMode extends Component {
|
||||||
findKeyBinding: this.handleFindKeyBinding
|
findKeyBinding: this.handleFindKeyBinding
|
||||||
},
|
},
|
||||||
|
|
||||||
search: {
|
searchResult: {
|
||||||
text: '',
|
text: '',
|
||||||
matches: null,
|
matches: null,
|
||||||
active: null // active search result
|
active: null // active search result
|
||||||
|
@ -287,13 +287,13 @@ export default class TreeMode extends Component {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.search !== false) {
|
if (this.props.searchResult !== false) {
|
||||||
// option search is true or undefined
|
// option search is true or undefined
|
||||||
items = items.concat([
|
items = items.concat([
|
||||||
h('div', {key: 'search', className: 'jsoneditor-menu-panel-right'},
|
h('div', {key: 'search', className: 'jsoneditor-menu-panel-right'},
|
||||||
h(Search, {
|
h(Search, {
|
||||||
text: this.state.search.text,
|
text: this.state.searchResult.text,
|
||||||
resultCount: this.state.search.matches ? this.state.search.matches.length : 0,
|
resultCount: this.state.searchResult.matches ? this.state.searchResult.matches.length : 0,
|
||||||
onChange: this.handleSearch,
|
onChange: this.handleSearch,
|
||||||
onNext: this.handleNext,
|
onNext: this.handleNext,
|
||||||
onPrevious: this.handlePrevious,
|
onPrevious: this.handlePrevious,
|
||||||
|
@ -584,20 +584,20 @@ export default class TreeMode extends Component {
|
||||||
|
|
||||||
handleSearch = (text) => {
|
handleSearch = (text) => {
|
||||||
// FIXME: also apply search when eson is changed
|
// FIXME: also apply search when eson is changed
|
||||||
const { eson, matches, active } = search(this.state.eson, text)
|
const { eson, searchResult } = search(this.state.eson, text)
|
||||||
if (matches.length > 0) {
|
if (searchResult.matches.length > 0) {
|
||||||
this.setState({
|
this.setState({
|
||||||
search: { text, active, matches },
|
eson: expandPath(eson, initial(searchResult.active.path)),
|
||||||
eson: expandPath(eson, initial(active.path))
|
searchResult
|
||||||
})
|
})
|
||||||
|
|
||||||
// scroll to active search result (on next tick, after this path has been expanded)
|
// scroll to active search result (on next tick, after this path has been expanded)
|
||||||
setTimeout(() => this.scrollTo(active.path))
|
setTimeout(() => this.scrollTo(searchResult.active.path))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.setState({
|
this.setState({
|
||||||
search: { text, active, matches },
|
eson,
|
||||||
eson
|
searchResult
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -610,22 +610,22 @@ export default class TreeMode extends Component {
|
||||||
handleNext = (event) => {
|
handleNext = (event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
if (this.state.search) {
|
if (this.state.searchResult) {
|
||||||
const { eson, active } = nextSearchResult(this.state.eson, this.state.search.matches, this.state.search.active)
|
const { eson, searchResult } = nextSearchResult(this.state.eson, this.state.searchResult)
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
search: setIn(this.state.search, ['active'], active),
|
eson,
|
||||||
eson
|
searchResult
|
||||||
})
|
})
|
||||||
|
|
||||||
// scroll to the active result (on next tick, after this path has been expanded)
|
// scroll to the active result (on next tick, after this path has been expanded)
|
||||||
// TODO: this code is duplicate with handlePrevious, move into a separate function
|
// TODO: this code is duplicate with handlePrevious, move into a separate function
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (active && active.path) {
|
if (searchResult.active && searchResult.active.path) {
|
||||||
this.scrollTo(active.path)
|
this.scrollTo(searchResult.active.path)
|
||||||
|
|
||||||
if (!searchHasFocus()) {
|
if (!searchHasFocus()) {
|
||||||
setSelection(this.refs.contents, active.path, active.area)
|
setSelection(this.refs.contents, searchResult.active.path, searchResult.active.area)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -635,21 +635,21 @@ export default class TreeMode extends Component {
|
||||||
handlePrevious = (event) => {
|
handlePrevious = (event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
if (this.state.search) {
|
if (this.state.searchResult) {
|
||||||
const { eson, active } = previousSearchResult(this.state.eson, this.state.search.matches, this.state.search.active)
|
const { eson, searchResult } = previousSearchResult(this.state.eson, this.state.searchResult)
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
search: setIn(this.state.search, ['active'], active),
|
eson,
|
||||||
eson
|
searchResult
|
||||||
})
|
})
|
||||||
|
|
||||||
// scroll to the active result (on next tick, after this path has been expanded)
|
// scroll to the active result (on next tick, after this path has been expanded)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (active && active.path) {
|
if (searchResult.active && searchResult.active.path) {
|
||||||
this.scrollTo(active.path)
|
this.scrollTo(searchResult.active.path)
|
||||||
|
|
||||||
if (!searchHasFocus()) {
|
if (!searchHasFocus()) {
|
||||||
setSelection(this.refs.contents, active.path, active.area)
|
setSelection(this.refs.contents, searchResult.active.path, searchResult.active.area)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -800,6 +800,7 @@ export default class TreeMode extends Component {
|
||||||
|
|
||||||
const result = patchEson(this.state.eson, historyItem.undo)
|
const result = patchEson(this.state.eson, historyItem.undo)
|
||||||
|
|
||||||
|
// FIXME: apply search
|
||||||
this.setState({
|
this.setState({
|
||||||
eson: result.data,
|
eson: result.data,
|
||||||
history,
|
history,
|
||||||
|
@ -818,6 +819,7 @@ export default class TreeMode extends Component {
|
||||||
|
|
||||||
const result = patchEson(this.state.eson, historyItem.redo)
|
const result = patchEson(this.state.eson, historyItem.redo)
|
||||||
|
|
||||||
|
// FIXME: apply search
|
||||||
this.setState({
|
this.setState({
|
||||||
eson: result.data,
|
eson: result.data,
|
||||||
history,
|
history,
|
||||||
|
@ -859,6 +861,7 @@ export default class TreeMode extends Component {
|
||||||
.concat(this.state.history.slice(this.state.historyIndex))
|
.concat(this.state.history.slice(this.state.historyIndex))
|
||||||
.slice(0, MAX_HISTORY_ITEMS)
|
.slice(0, MAX_HISTORY_ITEMS)
|
||||||
|
|
||||||
|
// FIXME: apply search
|
||||||
this.setState({
|
this.setState({
|
||||||
eson,
|
eson,
|
||||||
history,
|
history,
|
||||||
|
@ -867,6 +870,7 @@ export default class TreeMode extends Component {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// update data and don't store history
|
// update data and don't store history
|
||||||
|
// FIXME: apply search
|
||||||
this.setState({ eson })
|
this.setState({ eson })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -887,6 +891,7 @@ export default class TreeMode extends Component {
|
||||||
// TODO: document option expand
|
// TODO: document option expand
|
||||||
const expandCallback = this.props.expand || TreeMode.expandRoot
|
const expandCallback = this.props.expand || TreeMode.expandRoot
|
||||||
|
|
||||||
|
// FIXME: apply search
|
||||||
this.setState({
|
this.setState({
|
||||||
json: json,
|
json: json,
|
||||||
eson: expand(jsonToEson(json), expandCallback),
|
eson: expand(jsonToEson(json), expandCallback),
|
||||||
|
@ -902,7 +907,8 @@ export default class TreeMode extends Component {
|
||||||
* @returns {Object | Array | string | number | boolean | null} json
|
* @returns {Object | Array | string | number | boolean | null} json
|
||||||
*/
|
*/
|
||||||
get () {
|
get () {
|
||||||
return this.state.json
|
// FIXME: keep a copy of the json file up to date with the internal eson
|
||||||
|
return esonToJson(this.state.eson)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
71
src/eson.js
71
src/eson.js
|
@ -15,13 +15,11 @@ import initial from 'lodash/initial'
|
||||||
import last from 'lodash/last'
|
import last from 'lodash/last'
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ESON, ESONObject, ESONArrayItem, ESONPointer, Selection, ESONPath,
|
ESON, ESONPointer, Selection,
|
||||||
Path,
|
Path,
|
||||||
JSONPath, JSONType
|
JSONPath, JSONType
|
||||||
} from './types'
|
} from './types'
|
||||||
|
|
||||||
type RecurseCallback = (value: ESON, path: Path, root: ESON) => ESON
|
|
||||||
|
|
||||||
export const SELECTED = 1
|
export const SELECTED = 1
|
||||||
export const SELECTED_END = 2
|
export const SELECTED_END = 2
|
||||||
export const SELECTED_BEFORE = 3
|
export const SELECTED_BEFORE = 3
|
||||||
|
@ -245,7 +243,7 @@ export function cleanupMetaData(eson, field, ignorePaths = []) {
|
||||||
* Search some text in all properties and values
|
* Search some text in all properties and values
|
||||||
* @param {ESON} eson
|
* @param {ESON} eson
|
||||||
* @param {String} text Search text
|
* @param {String} text Search text
|
||||||
* @return {{eson: ESON, matches: ESONPointer[], active: ESONPointer}} Returns search result:
|
* @return {SearchResult} Returns search result:
|
||||||
* An updated eson object containing the search results,
|
* An updated eson object containing the search results,
|
||||||
* and an array with the paths of all matches
|
* and an array with the paths of all matches
|
||||||
*/
|
*/
|
||||||
|
@ -281,7 +279,14 @@ export function search (eson, text) {
|
||||||
return updatedValue
|
return updatedValue
|
||||||
})
|
})
|
||||||
|
|
||||||
return { eson: updatedEson, matches, active: matches[0] || null }
|
return {
|
||||||
|
eson: updatedEson,
|
||||||
|
searchResult: {
|
||||||
|
text,
|
||||||
|
matches,
|
||||||
|
active: matches[0] || null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -290,26 +295,24 @@ export function search (eson, text) {
|
||||||
* and return the last result as next.
|
* and return the last result as next.
|
||||||
*
|
*
|
||||||
* @param {ESON} eson
|
* @param {ESON} eson
|
||||||
* @param {ESONPointer[]} matches
|
* @param {SearchResult} searchResult
|
||||||
* @param {ESONPointer} active
|
* @return {{eson: ESON, searchResult: SearchResult}}
|
||||||
* @return {{eson: ESON, matches: ESONPointer[], active: ESONPointer}}
|
|
||||||
*/
|
*/
|
||||||
export function previousSearchResult (eson, matches, active) {
|
export function previousSearchResult (eson, searchResult) {
|
||||||
if (matches.length === 0) {
|
if (searchResult.matches.length === 0) {
|
||||||
return { eson, matches, active }
|
return { eson, searchResult }
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = matches.findIndex(searchResult => isEqual(searchResult, active))
|
const index = searchResult.matches.findIndex(match => isEqual(match, searchResult.active))
|
||||||
const previous = (index !== -1)
|
const previous = (index !== -1)
|
||||||
? index > 0
|
? index > 0
|
||||||
? matches[index - 1]
|
? searchResult.matches[index - 1]
|
||||||
: last(matches)
|
: last(searchResult.matches)
|
||||||
: matches[0]
|
: searchResult.matches[0]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
eson: setSearchStatus(setSearchStatus(eson, active, 'normal'), previous, 'active'),
|
eson: setSearchStatus(setSearchStatus(eson, searchResult.active, 'normal'), previous, 'active'),
|
||||||
matches,
|
searchResult: Object.assign({}, searchResult, { active: previous})
|
||||||
active: previous
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,26 +322,24 @@ export function previousSearchResult (eson, matches, active) {
|
||||||
* and return the first result as next.
|
* and return the first result as next.
|
||||||
*
|
*
|
||||||
* @param {ESON} eson
|
* @param {ESON} eson
|
||||||
* @param {ESONPointer[]} matches
|
* @param {SearchResult} searchResult
|
||||||
* @param {ESONPointer} active
|
* @return {{eson: ESON, searchResult: SearchResult}}
|
||||||
* @return {{eson: ESON, matches: ESONPointer[], active: ESONPointer}}
|
|
||||||
*/
|
*/
|
||||||
export function nextSearchResult (eson, matches, active) {
|
export function nextSearchResult (eson, searchResult) {
|
||||||
if (isEmpty(matches)) {
|
if (isEmpty(searchResult.matches)) {
|
||||||
return { eson, matches, active }
|
return { eson, searchResult }
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = matches.findIndex(match => isEqual(match, active))
|
const index = searchResult.matches.findIndex(match => isEqual(match, searchResult.active))
|
||||||
const next = (index !== -1)
|
const next = (index !== -1)
|
||||||
? index < matches.length - 1
|
? index < searchResult.matches.length - 1
|
||||||
? matches[index + 1]
|
? searchResult.matches[index + 1]
|
||||||
: matches[0]
|
: searchResult.matches[0]
|
||||||
: matches[0]
|
: searchResult.matches[0]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
eson: setSearchStatus(setSearchStatus(eson, active, 'normal'), next, 'active'),
|
eson: setSearchStatus(setSearchStatus(eson, searchResult.active, 'normal'), next, 'active'),
|
||||||
matches,
|
searchResult: Object.assign({}, searchResult, { active: next})
|
||||||
active: next
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,7 +464,7 @@ export function pathsFromSelection (eson, selection: Selection): JSONPath[] {
|
||||||
if (root[META].type === 'Object') {
|
if (root[META].type === 'Object') {
|
||||||
return times(maxIndex - minIndex, i => rootPath.concat(root[META].props[i + minIndex]))
|
return times(maxIndex - minIndex, i => rootPath.concat(root[META].props[i + minIndex]))
|
||||||
}
|
}
|
||||||
else { // root.type === 'Array'
|
else { // root[META].type === 'Array'
|
||||||
return times(maxIndex - minIndex, i => rootPath.concat(String(i + minIndex)))
|
return times(maxIndex - minIndex, i => rootPath.concat(String(i + minIndex)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -477,7 +478,7 @@ export function pathsFromSelection (eson, selection: Selection): JSONPath[] {
|
||||||
export function contentsFromPaths (data: ESON, paths: JSONPath[]) {
|
export function contentsFromPaths (data: ESON, paths: JSONPath[]) {
|
||||||
return paths.map(path => {
|
return paths.map(path => {
|
||||||
return {
|
return {
|
||||||
name: getIn(data, last(path)),
|
name: last(path),
|
||||||
value: esonToJson(getIn(data, path))
|
value: esonToJson(getIn(data, path))
|
||||||
// FIXME: also store the type and expanded state
|
// FIXME: also store the type and expanded state
|
||||||
}
|
}
|
||||||
|
@ -544,7 +545,7 @@ export function pathExists (eson, path) {
|
||||||
// index of an array
|
// index of an array
|
||||||
return pathExists(eson[parseInt(path[0])], path.slice(1))
|
return pathExists(eson[parseInt(path[0])], path.slice(1))
|
||||||
}
|
}
|
||||||
else { // eson.type === 'Object'
|
else { // Object
|
||||||
// object property. find the index of this property
|
// object property. find the index of this property
|
||||||
return pathExists(eson[path[0]], path.slice(1))
|
return pathExists(eson[path[0]], path.slice(1))
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,7 +177,7 @@ export function remove (data, path) {
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { // object.type === 'Object'
|
else { // parent[META].type === 'Object'
|
||||||
const prop = last(pathArray)
|
const prop = last(pathArray)
|
||||||
const index = parent[META].props.indexOf(prop)
|
const index = parent[META].props.indexOf(prop)
|
||||||
const nextProp = parent[META].props[index + 1] || null
|
const nextProp = parent[META].props[index + 1] || null
|
||||||
|
@ -221,7 +221,7 @@ export function add (data, path, value, options) {
|
||||||
if (parent[META].type === 'Array') {
|
if (parent[META].type === 'Array') {
|
||||||
updatedEson = updatePaths(insertAt(data, resolvedPath, value))
|
updatedEson = updatePaths(insertAt(data, resolvedPath, value))
|
||||||
}
|
}
|
||||||
else { // parent.type === 'Object'
|
else { // parent[META].type === 'Object'
|
||||||
updatedEson = updateIn(data, parentPath, (parent) => {
|
updatedEson = updateIn(data, parentPath, (parent) => {
|
||||||
const oldValue = getIn(data, pathArray)
|
const oldValue = getIn(data, pathArray)
|
||||||
const props = parent[META].props
|
const props = parent[META].props
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
*
|
*
|
||||||
* @typedef {string[]} Path
|
* @typedef {string[]} Path
|
||||||
*
|
*
|
||||||
|
* @typedef {{matches: ESONPointer[], active: ESONPointer, text: String}} SearchResult
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// FIXME: redefine all ESON related types
|
// FIXME: redefine all ESON related types
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
import { readFileSync } from 'fs'
|
import { readFileSync } from 'fs'
|
||||||
import test from 'ava'
|
import test from 'ava'
|
||||||
import { setIn, getIn, deleteIn } from '../src/utils/immutabilityHelpers'
|
import { setIn, getIn, deleteIn } from '../src/utils/immutabilityHelpers'
|
||||||
|
@ -243,10 +245,10 @@ test('search', t => {
|
||||||
"nill": null,
|
"nill": null,
|
||||||
"bool": false
|
"bool": false
|
||||||
})
|
})
|
||||||
const searchResult = search(eson, 'L')
|
const result = search(eson, 'L')
|
||||||
const esonWithSearch = searchResult.eson
|
const esonWithSearch = result.eson
|
||||||
const matches = searchResult.matches
|
const matches = result.searchResult.matches
|
||||||
const active = searchResult.active
|
const active = result.searchResult.active
|
||||||
|
|
||||||
t.deepEqual(matches, [
|
t.deepEqual(matches, [
|
||||||
{path: ['obj', 'arr', '2', 'last'], area: 'property'},
|
{path: ['obj', 'arr', '2', 'last'], area: 'property'},
|
||||||
|
@ -278,33 +280,33 @@ test('nextSearchResult', t => {
|
||||||
"nill": null,
|
"nill": null,
|
||||||
"bool": false
|
"bool": false
|
||||||
})
|
})
|
||||||
const searchResult = search(eson, 'A')
|
const first = search(eson, 'A')
|
||||||
|
|
||||||
t.deepEqual(searchResult.matches, [
|
t.deepEqual(first.searchResult.matches, [
|
||||||
{path: ['obj', 'arr'], area: 'property'},
|
{path: ['obj', 'arr'], area: 'property'},
|
||||||
{path: ['obj', 'arr', '2', 'last'], area: 'property'},
|
{path: ['obj', 'arr', '2', 'last'], area: 'property'},
|
||||||
{path: ['bool'], area: 'value'}
|
{path: ['bool'], area: 'value'}
|
||||||
])
|
])
|
||||||
|
|
||||||
t.deepEqual(searchResult.active, {path: ['obj', 'arr'], area: 'property'})
|
t.deepEqual(first.searchResult.active, {path: ['obj', 'arr'], area: 'property'})
|
||||||
t.is(getIn(searchResult.eson, ['obj', 'arr', META, 'searchProperty']), 'active')
|
t.is(getIn(first.eson, ['obj', 'arr', META, 'searchProperty']), 'active')
|
||||||
t.is(getIn(searchResult.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'normal')
|
t.is(getIn(first.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'normal')
|
||||||
t.is(getIn(searchResult.eson, ['bool', META, 'searchValue']), 'normal')
|
t.is(getIn(first.eson, ['bool', META, 'searchValue']), 'normal')
|
||||||
|
|
||||||
const second = nextSearchResult(searchResult.eson, searchResult.matches, searchResult.active)
|
const second = nextSearchResult(first.eson, first.searchResult)
|
||||||
t.deepEqual(second.active, {path: ['obj', 'arr', '2', 'last'], area: 'property'})
|
t.deepEqual(second.searchResult.active, {path: ['obj', 'arr', '2', 'last'], area: 'property'})
|
||||||
t.is(getIn(second.eson, ['obj', 'arr', META, 'searchProperty']), 'normal')
|
t.is(getIn(second.eson, ['obj', 'arr', META, 'searchProperty']), 'normal')
|
||||||
t.is(getIn(second.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'active')
|
t.is(getIn(second.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'active')
|
||||||
t.is(getIn(second.eson, ['bool', META, 'searchValue']), 'normal')
|
t.is(getIn(second.eson, ['bool', META, 'searchValue']), 'normal')
|
||||||
|
|
||||||
const third = nextSearchResult(second.eson, second.matches, second.active)
|
const third = nextSearchResult(second.eson, second.searchResult)
|
||||||
t.deepEqual(third.active, {path: ['bool'], area: 'value'})
|
t.deepEqual(third.searchResult.active, {path: ['bool'], area: 'value'})
|
||||||
t.is(getIn(third.eson, ['obj', 'arr', META, 'searchProperty']), 'normal')
|
t.is(getIn(third.eson, ['obj', 'arr', META, 'searchProperty']), 'normal')
|
||||||
t.is(getIn(third.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'normal')
|
t.is(getIn(third.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'normal')
|
||||||
t.is(getIn(third.eson, ['bool', META, 'searchValue']), 'active')
|
t.is(getIn(third.eson, ['bool', META, 'searchValue']), 'active')
|
||||||
|
|
||||||
const wrappedAround = nextSearchResult(third.eson, third.matches, third.active)
|
const wrappedAround = nextSearchResult(third.eson, third.searchResult)
|
||||||
t.deepEqual(wrappedAround.active, {path: ['obj', 'arr'], area: 'property'})
|
t.deepEqual(wrappedAround.searchResult.active, {path: ['obj', 'arr'], area: 'property'})
|
||||||
t.is(getIn(wrappedAround.eson, ['obj', 'arr', META, 'searchProperty']), 'active')
|
t.is(getIn(wrappedAround.eson, ['obj', 'arr', META, 'searchProperty']), 'active')
|
||||||
t.is(getIn(wrappedAround.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'normal')
|
t.is(getIn(wrappedAround.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'normal')
|
||||||
t.is(getIn(wrappedAround.eson, ['bool', META, 'searchValue']), 'normal')
|
t.is(getIn(wrappedAround.eson, ['bool', META, 'searchValue']), 'normal')
|
||||||
|
@ -319,33 +321,33 @@ test('previousSearchResult', t => {
|
||||||
"nill": null,
|
"nill": null,
|
||||||
"bool": false
|
"bool": false
|
||||||
})
|
})
|
||||||
const searchResult = search(eson, 'A')
|
const init = search(eson, 'A')
|
||||||
|
|
||||||
t.deepEqual(searchResult.matches, [
|
t.deepEqual(init.searchResult.matches, [
|
||||||
{path: ['obj', 'arr'], area: 'property'},
|
{path: ['obj', 'arr'], area: 'property'},
|
||||||
{path: ['obj', 'arr', '2', 'last'], area: 'property'},
|
{path: ['obj', 'arr', '2', 'last'], area: 'property'},
|
||||||
{path: ['bool'], area: 'value'}
|
{path: ['bool'], area: 'value'}
|
||||||
])
|
])
|
||||||
|
|
||||||
t.deepEqual(searchResult.active, {path: ['obj', 'arr'], area: 'property'})
|
t.deepEqual(init.searchResult.active, {path: ['obj', 'arr'], area: 'property'})
|
||||||
t.is(getIn(searchResult.eson, ['obj', 'arr', META, 'searchProperty']), 'active')
|
t.is(getIn(init.eson, ['obj', 'arr', META, 'searchProperty']), 'active')
|
||||||
t.is(getIn(searchResult.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'normal')
|
t.is(getIn(init.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'normal')
|
||||||
t.is(getIn(searchResult.eson, ['bool', META, 'searchValue']), 'normal')
|
t.is(getIn(init.eson, ['bool', META, 'searchValue']), 'normal')
|
||||||
|
|
||||||
const third = previousSearchResult(searchResult.eson, searchResult.matches, searchResult.active)
|
const third = previousSearchResult(init.eson, init.searchResult)
|
||||||
t.deepEqual(third.active, {path: ['bool'], area: 'value'})
|
t.deepEqual(third.searchResult.active, {path: ['bool'], area: 'value'})
|
||||||
t.is(getIn(third.eson, ['obj', 'arr', META, 'searchProperty']), 'normal')
|
t.is(getIn(third.eson, ['obj', 'arr', META, 'searchProperty']), 'normal')
|
||||||
t.is(getIn(third.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'normal')
|
t.is(getIn(third.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'normal')
|
||||||
t.is(getIn(third.eson, ['bool', META, 'searchValue']), 'active')
|
t.is(getIn(third.eson, ['bool', META, 'searchValue']), 'active')
|
||||||
|
|
||||||
const second = previousSearchResult(third.eson, third.matches, third.active)
|
const second = previousSearchResult(third.eson, third.searchResult)
|
||||||
t.deepEqual(second.active, {path: ['obj', 'arr', '2', 'last'], area: 'property'})
|
t.deepEqual(second.searchResult.active, {path: ['obj', 'arr', '2', 'last'], area: 'property'})
|
||||||
t.is(getIn(second.eson, ['obj', 'arr', META, 'searchProperty']), 'normal')
|
t.is(getIn(second.eson, ['obj', 'arr', META, 'searchProperty']), 'normal')
|
||||||
t.is(getIn(second.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'active')
|
t.is(getIn(second.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'active')
|
||||||
t.is(getIn(second.eson, ['bool', META, 'searchValue']), 'normal')
|
t.is(getIn(second.eson, ['bool', META, 'searchValue']), 'normal')
|
||||||
|
|
||||||
const first = previousSearchResult(second.eson, second.matches, second.active)
|
const first = previousSearchResult(second.eson, second.searchResult)
|
||||||
t.deepEqual(first.active, {path: ['obj', 'arr'], area: 'property'})
|
t.deepEqual(first.searchResult.active, {path: ['obj', 'arr'], area: 'property'})
|
||||||
t.is(getIn(first.eson, ['obj', 'arr', META, 'searchProperty']), 'active')
|
t.is(getIn(first.eson, ['obj', 'arr', META, 'searchProperty']), 'active')
|
||||||
t.is(getIn(first.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'normal')
|
t.is(getIn(first.eson, ['obj', 'arr', '2', 'last', META, 'searchProperty']), 'normal')
|
||||||
t.is(getIn(first.eson, ['bool', META, 'searchValue']), 'normal')
|
t.is(getIn(first.eson, ['bool', META, 'searchValue']), 'normal')
|
||||||
|
|
Loading…
Reference in New Issue