Replace/insert working. Insert before instead of after
This commit is contained in:
parent
2e75e5a9cf
commit
bac12dcc5a
|
@ -92,17 +92,18 @@ export function changeType (data, path, type) {
|
||||||
* and object property
|
* and object property
|
||||||
*
|
*
|
||||||
* @param {ESON} data
|
* @param {ESON} data
|
||||||
* @param {ESONSelection} selection
|
* @param {Selection} selection
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
export function duplicate (data, selection) {
|
export function duplicate (data, selection) {
|
||||||
// console.log('duplicate', path)
|
// console.log('duplicate', path)
|
||||||
|
if (!selection.start || !selection.end) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
const rootPath = findRootPath(selection)
|
const rootPath = findRootPath(selection)
|
||||||
const start = selection.start.path[rootPath.length]
|
|
||||||
const end = selection.end.path[rootPath.length]
|
|
||||||
const root = getIn(data, toEsonPath(data, rootPath))
|
const root = getIn(data, toEsonPath(data, rootPath))
|
||||||
const { maxIndex } = findSelectionIndices(root, start, end)
|
const { maxIndex } = findSelectionIndices(root, rootPath, selection)
|
||||||
const paths = pathsFromSelection(data, selection)
|
const paths = pathsFromSelection(data, selection)
|
||||||
|
|
||||||
if (root.type === 'Array') {
|
if (root.type === 'Array') {
|
||||||
|
@ -113,7 +114,6 @@ export function duplicate (data, selection) {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
else { // object.type === 'Object'
|
else { // object.type === 'Object'
|
||||||
const { maxIndex } = findSelectionIndices(root, start, end)
|
|
||||||
const nextProp = root.props && root.props[maxIndex]
|
const nextProp = root.props && root.props[maxIndex]
|
||||||
const before = nextProp ? nextProp.name : null
|
const before = nextProp ? nextProp.name : null
|
||||||
|
|
||||||
|
@ -133,53 +133,6 @@ export function duplicate (data, selection) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a JSONPatch for an insert action.
|
|
||||||
*
|
|
||||||
* This function needs the current data in order to be able to determine
|
|
||||||
* a unique property name for the inserted node in case of duplicating
|
|
||||||
* and object property
|
|
||||||
*
|
|
||||||
* @param {ESON} data
|
|
||||||
* @param {Path} path
|
|
||||||
* @param {ESONType} type
|
|
||||||
* @return {Array}
|
|
||||||
*/
|
|
||||||
export function insert (data, path, type) {
|
|
||||||
// console.log('insert', path, type)
|
|
||||||
|
|
||||||
const parentPath = path.slice(0, path.length - 1)
|
|
||||||
const esonPath = toEsonPath(data, parentPath)
|
|
||||||
const parent = getIn(data, esonPath)
|
|
||||||
const value = createEntry(type)
|
|
||||||
|
|
||||||
if (parent.type === 'Array') {
|
|
||||||
const index = parseInt(path[path.length - 1]) + 1
|
|
||||||
return [{
|
|
||||||
op: 'add',
|
|
||||||
path: compileJSONPointer(parentPath.concat(index)),
|
|
||||||
value,
|
|
||||||
jsoneditor: {
|
|
||||||
type
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
else { // object.type === 'Object'
|
|
||||||
const prop = path[path.length - 1]
|
|
||||||
const newProp = findUniqueName('', parent.props.map(p => p.name))
|
|
||||||
|
|
||||||
return [{
|
|
||||||
op: 'add',
|
|
||||||
path: compileJSONPointer(parentPath.concat(newProp)),
|
|
||||||
value,
|
|
||||||
jsoneditor: {
|
|
||||||
type,
|
|
||||||
before: findNextProp(parent, prop)
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a JSONPatch for an insert action.
|
* Create a JSONPatch for an insert action.
|
||||||
*
|
*
|
||||||
|
@ -233,17 +186,14 @@ export function insertBefore (data, path, values) { // TODO: find a better name
|
||||||
* and object property
|
* and object property
|
||||||
*
|
*
|
||||||
* @param {ESON} data
|
* @param {ESON} data
|
||||||
* @param {ESONSelection} selection
|
* @param {Selection} selection
|
||||||
* @param {Array.<{name?: string, value: JSONType, type?: ESONType}>} values
|
* @param {Array.<{name?: string, value: JSONType, type?: ESONType}>} values
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
export function replace (data, selection, values) { // TODO: find a better name and define datastructure for values
|
export function replace (data, selection, values) { // TODO: find a better name and define datastructure for values
|
||||||
|
|
||||||
const rootPath = findRootPath(selection)
|
const rootPath = findRootPath(selection)
|
||||||
const start = selection.start.path[rootPath.length]
|
|
||||||
const end = selection.end.path[rootPath.length]
|
|
||||||
const root = getIn(data, toEsonPath(data, rootPath))
|
const root = getIn(data, toEsonPath(data, rootPath))
|
||||||
const { minIndex, maxIndex } = findSelectionIndices(root, start, end)
|
const { minIndex, maxIndex } = findSelectionIndices(root, rootPath, selection)
|
||||||
|
|
||||||
if (root.type === 'Array') {
|
if (root.type === 'Array') {
|
||||||
const removeActions = removeAll(pathsFromSelection(data, selection))
|
const removeActions = removeAll(pathsFromSelection(data, selection))
|
||||||
|
|
|
@ -16,15 +16,15 @@ import type { ESONObjectProperty, ESON, SearchResultStatus, Path } from '../type
|
||||||
const SELECTED_CLASS_NAMES = {
|
const SELECTED_CLASS_NAMES = {
|
||||||
[SELECTED]: ' jsoneditor-selected',
|
[SELECTED]: ' jsoneditor-selected',
|
||||||
[SELECTED_END]: ' jsoneditor-selected jsoneditor-selected-end',
|
[SELECTED_END]: ' jsoneditor-selected jsoneditor-selected-end',
|
||||||
[SELECTED_AFTER]: ' jsoneditor-selected jsoneditor-selected-after',
|
[SELECTED_AFTER]: ' jsoneditor-selected jsoneditor-selected-insert-area',
|
||||||
[SELECTED_BEFORE]: ' jsoneditor-selected jsoneditor-selected-before',
|
[SELECTED_BEFORE]: ' jsoneditor-selected jsoneditor-selected-insert-area',
|
||||||
}
|
}
|
||||||
|
|
||||||
const HOVERED_CLASS_NAMES = {
|
const HOVERED_CLASS_NAMES = {
|
||||||
[SELECTED]: ' jsoneditor-hover',
|
[SELECTED]: ' jsoneditor-hover',
|
||||||
[SELECTED_END]: ' jsoneditor-hover jsoneditor-hover-end',
|
[SELECTED_END]: ' jsoneditor-hover jsoneditor-hover-end',
|
||||||
[SELECTED_AFTER]: ' jsoneditor-hover jsoneditor-hover-after',
|
[SELECTED_AFTER]: ' jsoneditor-hover jsoneditor-hover-insert-area',
|
||||||
[SELECTED_BEFORE]: ' jsoneditor-hover jsoneditor-hover-before',
|
[SELECTED_BEFORE]: ' jsoneditor-hover jsoneditor-hover-insert-area',
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class JSONNode extends PureComponent {
|
export default class JSONNode extends PureComponent {
|
||||||
|
@ -196,7 +196,7 @@ export default class JSONNode extends PureComponent {
|
||||||
])
|
])
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const insertArea = this.renderInsertArea()
|
const insertArea = this.renderInsertBeforeArea()
|
||||||
|
|
||||||
return h('div', {
|
return h('div', {
|
||||||
'data-path': compileJSONPointer(this.props.path),
|
'data-path': compileJSONPointer(this.props.path),
|
||||||
|
@ -206,8 +206,8 @@ export default class JSONNode extends PureComponent {
|
||||||
}, [node, floatingMenu, insertArea])
|
}, [node, floatingMenu, insertArea])
|
||||||
}
|
}
|
||||||
|
|
||||||
renderInsertArea () {
|
renderInsertBeforeArea () {
|
||||||
const floatingMenu = (this.props.data.selected === SELECTED_AFTER)
|
const floatingMenu = (this.props.data.selected === SELECTED_BEFORE)
|
||||||
? this.renderFloatingMenu([
|
? this.renderFloatingMenu([
|
||||||
{type: 'insertStructure'},
|
{type: 'insertStructure'},
|
||||||
{type: 'insertValue'},
|
{type: 'insertValue'},
|
||||||
|
@ -220,7 +220,7 @@ export default class JSONNode extends PureComponent {
|
||||||
return h('div', {
|
return h('div', {
|
||||||
key: 'menu',
|
key: 'menu',
|
||||||
className: 'jsoneditor-insert-area',
|
className: 'jsoneditor-insert-area',
|
||||||
'data-area': 'after'
|
'data-area': 'before'
|
||||||
}, [floatingMenu])
|
}, [floatingMenu])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ import {
|
||||||
import { createFindKeyBinding } from '../utils/keyBindings'
|
import { createFindKeyBinding } from '../utils/keyBindings'
|
||||||
import { KEY_BINDINGS } from '../constants'
|
import { KEY_BINDINGS } from '../constants'
|
||||||
|
|
||||||
import type { ESON, ESONPatch, JSONPath, ESONSelection, ESONPointer } from '../types'
|
import type { ESON, ESONPatch, JSONPath, Selection, ESONPointer } from '../types'
|
||||||
|
|
||||||
const AJV_OPTIONS = {
|
const AJV_OPTIONS = {
|
||||||
allErrors: true,
|
allErrors: true,
|
||||||
|
@ -348,17 +348,21 @@ export default class TreeMode extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInsert = (path, type) => {
|
handleInsert = (path, type) => {
|
||||||
this.handlePatch(insert(this.state.data, path, createEntry(type), type))
|
this.handlePatch(insertBefore(this.state.data, path, [{
|
||||||
|
type,
|
||||||
|
name: '',
|
||||||
|
value: createEntry(type)
|
||||||
|
}]))
|
||||||
|
|
||||||
this.setState({ selection : null }) // TODO: select the inserted entry
|
this.setState({ selection : null }) // TODO: select the inserted entry
|
||||||
|
|
||||||
// apply focus to new node
|
// apply focus to new node
|
||||||
this.focusToNext(path)
|
this.focusToPrevious(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInsertStructure = (path) => {
|
handleInsertStructure = (path) => {
|
||||||
// TODO: implement handleInsertStructure
|
// TODO: implement handleInsertStructure
|
||||||
console.log('handleInsertStructure', path)
|
console.log('TODO: handleInsertStructure', path)
|
||||||
alert('not yet implemented...')
|
alert('not yet implemented...')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -456,7 +460,7 @@ export default class TreeMode extends Component {
|
||||||
handleKeyDownDuplicate = (event) => {
|
handleKeyDownDuplicate = (event) => {
|
||||||
const path = this.findDataPathFromElement(event.target)
|
const path = this.findDataPathFromElement(event.target)
|
||||||
if (path) {
|
if (path) {
|
||||||
const selection = { start: {path}, end: {path} }
|
const selection = { start: path, end: path }
|
||||||
this.handlePatch(duplicate(this.state.data, selection))
|
this.handlePatch(duplicate(this.state.data, selection))
|
||||||
|
|
||||||
// apply focus to the duplicated node
|
// apply focus to the duplicated node
|
||||||
|
@ -510,11 +514,10 @@ export default class TreeMode extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move focus to the next search result
|
* Move focus to the next node
|
||||||
* @param {Path} path
|
* @param {Path} path
|
||||||
*/
|
*/
|
||||||
focusToNext (path) {
|
focusToNext (path) {
|
||||||
// apply focus to new element
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const element = findNode(this.refs.contents, path)
|
const element = findNode(this.refs.contents, path)
|
||||||
if (element) {
|
if (element) {
|
||||||
|
@ -523,12 +526,24 @@ export default class TreeMode extends Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move focus to the previous node
|
||||||
|
* @param {Path} path
|
||||||
|
*/
|
||||||
|
focusToPrevious (path) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const element = findNode(this.refs.contents, path)
|
||||||
|
if (element) {
|
||||||
|
moveUp(element, 'property')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
handleSort = (path, order = null) => {
|
handleSort = (path, order = null) => {
|
||||||
this.handlePatch(sort(this.state.data, path, order))
|
this.handlePatch(sort(this.state.data, path, order))
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelect = (selection: ESONSelection) => {
|
handleSelect = (selection: Selection) => {
|
||||||
console.log('handleSelect', selection)
|
|
||||||
this.setState({ selection })
|
this.setState({ selection })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -664,7 +679,7 @@ export default class TreeMode extends Component {
|
||||||
(event.target.contentEditable !== 'true')
|
(event.target.contentEditable !== 'true')
|
||||||
|
|
||||||
if (clickedOnEmptySpace && pointer) {
|
if (clickedOnEmptySpace && pointer) {
|
||||||
this.setState({ selection: {start: pointer, end: pointer}})
|
this.setState({ selection: this.selectionFromESONPointer(pointer)})
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.setState({ selection: null })
|
this.setState({ selection: null })
|
||||||
|
@ -672,12 +687,13 @@ export default class TreeMode extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePan = (event) => {
|
handlePan = (event) => {
|
||||||
|
const selection = this.state.selection
|
||||||
const path = this.findDataPathFromElement(event.target.firstChild)
|
const path = this.findDataPathFromElement(event.target.firstChild)
|
||||||
if (path && this.state.selection && !isEqual(path, this.state.selection.end.path)) {
|
if (path && selection && !isEqual(path, selection.end)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
selection: {
|
selection: {
|
||||||
start: this.state.selection.start,
|
start: selection.start || selection.before || selection.after,
|
||||||
end: {path}
|
end: path
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -711,6 +727,18 @@ export default class TreeMode extends Component {
|
||||||
return path ? { path, area } : null
|
return path ? { path, area } : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectionFromESONPointer (pointer: ESONPointer) : Selection {
|
||||||
|
if (pointer.area === 'after') {
|
||||||
|
return {after: pointer.path}
|
||||||
|
}
|
||||||
|
else if (pointer.area === 'before') {
|
||||||
|
return {before: pointer.path}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return {start: pointer.path, end: pointer.path}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scroll the window vertically to the node with given path
|
* Scroll the window vertically to the node with given path
|
||||||
* @param {Path} path
|
* @param {Path} path
|
||||||
|
@ -770,7 +798,6 @@ export default class TreeMode extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
undo = () => {
|
undo = () => {
|
||||||
console.log('undo')
|
|
||||||
if (this.canUndo()) {
|
if (this.canUndo()) {
|
||||||
const history = this.state.history
|
const history = this.state.history
|
||||||
const historyIndex = this.state.historyIndex
|
const historyIndex = this.state.historyIndex
|
||||||
|
|
|
@ -12,6 +12,10 @@
|
||||||
<script src="./resources/largeJson.js"></script>
|
<script src="./resources/largeJson.js"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
body, input, select {
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 11pt;
|
||||||
|
}
|
||||||
#container {
|
#container {
|
||||||
height: 400px;
|
height: 400px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -35,6 +39,10 @@
|
||||||
<option value="view">view</option>
|
<option value="view">view</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="logEvents" > Log events
|
||||||
|
</label>
|
||||||
</p>
|
</p>
|
||||||
<div id="container"></div>
|
<div id="container"></div>
|
||||||
|
|
||||||
|
@ -57,22 +65,22 @@
|
||||||
const options = {
|
const options = {
|
||||||
name: 'myObject',
|
name: 'myObject',
|
||||||
onPatch: function (patch, revert) {
|
onPatch: function (patch, revert) {
|
||||||
console.log('onPatch patch=', patch, ', revert=', revert)
|
log('onPatch patch=', patch, ', revert=', revert)
|
||||||
window.patch = patch
|
window.patch = patch
|
||||||
window.revert = revert
|
window.revert = revert
|
||||||
},
|
},
|
||||||
onPatchText: function (patch, revert) {
|
onPatchText: function (patch, revert) {
|
||||||
// FIXME: implement onPatchText
|
// FIXME: implement onPatchText
|
||||||
console.log('onPatchText patch=', patch, ', revert=', revert)
|
log('onPatchText patch=', patch, ', revert=', revert)
|
||||||
},
|
},
|
||||||
onChange: function (json) {
|
onChange: function (json) {
|
||||||
console.log('onChange json=', json)
|
log('onChange json=', json)
|
||||||
},
|
},
|
||||||
onChangeText: function (text) {
|
onChangeText: function (text) {
|
||||||
console.log('onChangeText', text)
|
log('onChangeText', text)
|
||||||
},
|
},
|
||||||
onChangeMode: function (mode, prevMode) {
|
onChangeMode: function (mode, prevMode) {
|
||||||
console.log('switched mode from', prevMode, 'to', mode)
|
log('switched mode from', prevMode, 'to', mode)
|
||||||
document.getElementById('mode').value = mode
|
document.getElementById('mode').value = mode
|
||||||
},
|
},
|
||||||
onError: function (err) {
|
onError: function (err) {
|
||||||
|
@ -159,6 +167,12 @@
|
||||||
editor.setMode(mode)
|
editor.setMode(mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function log (...args) {
|
||||||
|
if (document.getElementById('logEvents').checked) {
|
||||||
|
console.log(...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
52
src/eson.js
52
src/eson.js
|
@ -13,7 +13,7 @@ import initial from 'lodash/initial'
|
||||||
import last from 'lodash/last'
|
import last from 'lodash/last'
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ESON, ESONObject, ESONArrayItem, ESONPointer, ESONSelection, ESONType, ESONPath,
|
ESON, ESONObject, ESONArrayItem, ESONPointer, Selection, ESONType, ESONPath,
|
||||||
Path,
|
Path,
|
||||||
JSONPath, JSONType
|
JSONPath, JSONType
|
||||||
} from './types'
|
} from './types'
|
||||||
|
@ -357,19 +357,25 @@ export function applySearchResults (eson: ESON, searchResults: ESONPointer[], ac
|
||||||
/**
|
/**
|
||||||
* Merge searchResults into the eson object
|
* Merge searchResults into the eson object
|
||||||
*/
|
*/
|
||||||
export function applySelection (eson: ESON, selection: ESONSelection) {
|
export function applySelection (eson: ESON, selection: Selection) {
|
||||||
if (!selection) {
|
if (!selection) {
|
||||||
return eson
|
return eson
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selection.before) {
|
||||||
|
const esonPath = toEsonPath(eson, selection.before)
|
||||||
|
return setIn(eson, esonPath.concat('selected'), SELECTED_BEFORE)
|
||||||
|
}
|
||||||
|
else if (selection.after) {
|
||||||
|
const esonPath = toEsonPath(eson, selection.after)
|
||||||
|
return setIn(eson, esonPath.concat('selected'), SELECTED_AFTER)
|
||||||
|
}
|
||||||
|
else { // selection.start and selection.end
|
||||||
// find the parent node shared by both start and end of the selection
|
// find the parent node shared by both start and end of the selection
|
||||||
const rootPath = findRootPath(selection)
|
const rootPath = findRootPath(selection)
|
||||||
const rootEsonPath = toEsonPath(eson, rootPath)
|
|
||||||
|
|
||||||
return updateIn(eson, rootEsonPath, (root) => {
|
return updateIn(eson, toEsonPath(eson, rootPath), (root) => {
|
||||||
const start = selection.start.path[rootPath.length]
|
const { minIndex, maxIndex } = findSelectionIndices(root, rootPath, selection)
|
||||||
const end = selection.end.path[rootPath.length]
|
|
||||||
const { minIndex, maxIndex } = findSelectionIndices(root, start, end)
|
|
||||||
|
|
||||||
const childsKey = (root.type === 'Object') ? 'props' : 'items' // property name of the array with props/items
|
const childsKey = (root.type === 'Object') ? 'props' : 'items' // property name of the array with props/items
|
||||||
const childsBefore = root[childsKey].slice(0, minIndex)
|
const childsBefore = root[childsKey].slice(0, minIndex)
|
||||||
|
@ -381,19 +387,24 @@ export function applySelection (eson: ESON, selection: ESONSelection) {
|
||||||
return setIn(root, [childsKey], childsBefore.concat(childsUpdated, childsAfter))
|
return setIn(root, [childsKey], childsBefore.concat(childsUpdated, childsAfter))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the min and max index of a start and end child.
|
* Find the min and max index of a start and end child.
|
||||||
* Start and end can be a property name in case of an Object,
|
* Start and end can be a property name in case of an Object,
|
||||||
* or a matrix index (string with a number) in case of an Array.
|
* or a matrix index (string with a number) in case of an Array.
|
||||||
*/
|
*/
|
||||||
export function findSelectionIndices (root: ESON, start: string, end: string) : { minIndex: number, maxIndex: number } {
|
export function findSelectionIndices (root: ESON, rootPath: JSONPath, selection: Selection) : { minIndex: number, maxIndex: number } {
|
||||||
|
const start = (selection.after || selection.before || selection.start)[rootPath.length]
|
||||||
|
const end = (selection.after || selection.before || selection.end)[rootPath.length]
|
||||||
|
|
||||||
// if no object we assume it's an Array
|
// if no object we assume it's an Array
|
||||||
const startIndex = root.type === 'Object' ? findPropertyIndex(root, start) : parseInt(start)
|
const startIndex = root.type === 'Object' ? findPropertyIndex(root, start) : parseInt(start)
|
||||||
const endIndex = root.type === 'Object' ? findPropertyIndex(root, end) : parseInt(end)
|
const endIndex = root.type === 'Object' ? findPropertyIndex(root, end) : parseInt(end)
|
||||||
|
|
||||||
const minIndex = Math.min(startIndex, endIndex)
|
const minIndex = Math.min(startIndex, endIndex)
|
||||||
const maxIndex = Math.max(startIndex, endIndex) + 1 // include max index itself
|
const maxIndex = Math.max(startIndex, endIndex) +
|
||||||
|
((selection.after || selection.before) ? 0 : 1) // include max index itself
|
||||||
|
|
||||||
return { minIndex, maxIndex }
|
return { minIndex, maxIndex }
|
||||||
}
|
}
|
||||||
|
@ -401,15 +412,12 @@ export function findSelectionIndices (root: ESON, start: string, end: string) :
|
||||||
/**
|
/**
|
||||||
* Get the JSON paths from a selection, sorted from first to last
|
* Get the JSON paths from a selection, sorted from first to last
|
||||||
*/
|
*/
|
||||||
export function pathsFromSelection (eson: ESON, selection: ESONSelection): JSONPath[] {
|
export function pathsFromSelection (eson: ESON, selection: Selection): JSONPath[] {
|
||||||
// find the parent node shared by both start and end of the selection
|
// find the parent node shared by both start and end of the selection
|
||||||
const rootPath = findRootPath(selection)
|
const rootPath = findRootPath(selection)
|
||||||
const rootEsonPath = toEsonPath(eson, rootPath)
|
const root = getIn(eson, toEsonPath(eson, rootPath))
|
||||||
|
|
||||||
const root = getIn(eson, rootEsonPath)
|
const { minIndex, maxIndex } = findSelectionIndices(root, rootPath, selection)
|
||||||
const start = selection.start.path[rootPath.length]
|
|
||||||
const end = selection.end.path[rootPath.length]
|
|
||||||
const { minIndex, maxIndex } = findSelectionIndices(root, start, end)
|
|
||||||
|
|
||||||
if (root.type === 'Object') {
|
if (root.type === 'Object') {
|
||||||
return times(maxIndex - minIndex, i => rootPath.concat(root.props[i + minIndex].name))
|
return times(maxIndex - minIndex, i => rootPath.concat(root.props[i + minIndex].name))
|
||||||
|
@ -443,10 +451,17 @@ export function contentsFromPaths (data: ESON, paths: JSONPath[]) {
|
||||||
* @return {JSONPath}
|
* @return {JSONPath}
|
||||||
*/
|
*/
|
||||||
export function findRootPath(selection) {
|
export function findRootPath(selection) {
|
||||||
const sharedPath = findSharedPath(selection.start.path, selection.end.path)
|
if (selection.before) {
|
||||||
|
return initial(selection.before)
|
||||||
|
}
|
||||||
|
else if (selection.after) {
|
||||||
|
return initial(selection.after)
|
||||||
|
}
|
||||||
|
else { // .start and .end
|
||||||
|
const sharedPath = findSharedPath(selection.start, selection.end)
|
||||||
|
|
||||||
if (sharedPath.length === selection.start.path.length &&
|
if (sharedPath.length === selection.start.length &&
|
||||||
sharedPath.length === selection.end.path.length) {
|
sharedPath.length === selection.end.length) {
|
||||||
// there is just one node selected, return it's parent
|
// there is just one node selected, return it's parent
|
||||||
return initial(sharedPath)
|
return initial(sharedPath)
|
||||||
}
|
}
|
||||||
|
@ -454,6 +469,7 @@ export function findRootPath(selection) {
|
||||||
return sharedPath
|
return sharedPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the common path of two paths.
|
* Find the common path of two paths.
|
||||||
|
|
|
@ -573,7 +573,7 @@ div.jsoneditor-node-container {
|
||||||
&.jsoneditor-hover {
|
&.jsoneditor-hover {
|
||||||
background-color: @hoverAndSelectedColor;
|
background-color: @hoverAndSelectedColor;
|
||||||
|
|
||||||
&.jsoneditor-hover-after {
|
&.jsoneditor-hover-insert-area {
|
||||||
background-color: @selectedColor;
|
background-color: @selectedColor;
|
||||||
|
|
||||||
div.jsoneditor-insert-area {
|
div.jsoneditor-insert-area {
|
||||||
|
@ -581,7 +581,7 @@ div.jsoneditor-node-container {
|
||||||
background-color: @hoverColor;
|
background-color: @hoverColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.jsoneditor-selected-after {
|
&.jsoneditor-selected-insert-area {
|
||||||
div.jsoneditor-insert-area {
|
div.jsoneditor-insert-area {
|
||||||
border: 1px dashed #f4af41;
|
border: 1px dashed #f4af41;
|
||||||
background-color: @hoverAndSelectedColor;
|
background-color: @hoverAndSelectedColor;
|
||||||
|
@ -590,13 +590,13 @@ div.jsoneditor-node-container {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.jsoneditor-selected-after {
|
&.jsoneditor-selected-insert-area {
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
|
|
||||||
&.jsoneditor-hover {
|
&.jsoneditor-hover {
|
||||||
background-color: @hoverColor;
|
background-color: @hoverColor;
|
||||||
|
|
||||||
&.jsoneditor-hover-after {
|
&.jsoneditor-hover-insert-area {
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -611,7 +611,7 @@ div.jsoneditor-node-container {
|
||||||
.jsoneditor-hover {
|
.jsoneditor-hover {
|
||||||
background-color: @hoverAndSelectedColor;
|
background-color: @hoverAndSelectedColor;
|
||||||
|
|
||||||
&.jsoneditor-hover-after {
|
&.jsoneditor-hover-insert-area {
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
|
|
||||||
div.jsoneditor-insert-area {
|
div.jsoneditor-insert-area {
|
||||||
|
@ -625,7 +625,7 @@ div.jsoneditor-node-container {
|
||||||
&.jsoneditor-hover {
|
&.jsoneditor-hover {
|
||||||
background-color: @hoverColor;
|
background-color: @hoverColor;
|
||||||
|
|
||||||
&.jsoneditor-hover-after {
|
&.jsoneditor-hover-insert-area {
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
|
|
||||||
div.jsoneditor-insert-area {
|
div.jsoneditor-insert-area {
|
||||||
|
@ -642,7 +642,7 @@ div.jsoneditor-node-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: @height;
|
height: @height;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: -@height/2;
|
top: -@height/2;
|
||||||
border: 1px transparent;
|
border: 1px transparent;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
z-index: 1; // must be on top of next node, it overlaps a bit
|
z-index: 1; // must be on top of next node, it overlaps a bit
|
||||||
|
|
10
src/types.js
10
src/types.js
|
@ -33,7 +33,7 @@ export type JSONArrayType = JSONType[]
|
||||||
/********************** TYPES FOR THE ESON OBJECT MODEL *************************/
|
/********************** TYPES FOR THE ESON OBJECT MODEL *************************/
|
||||||
|
|
||||||
export type SearchResultStatus = 'normal' | 'active'
|
export type SearchResultStatus = 'normal' | 'active'
|
||||||
export type ESONPointerArea = 'value' | 'property' | 'before' | 'after'
|
export type ESONPointerArea = 'value' | 'property'
|
||||||
|
|
||||||
export type ESONObjectProperty = {
|
export type ESONObjectProperty = {
|
||||||
id: number,
|
id: number,
|
||||||
|
@ -81,9 +81,11 @@ export type ESONPointer = {
|
||||||
area?: ESONPointerArea
|
area?: ESONPointerArea
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ESONSelection = {
|
export type Selection = {
|
||||||
start: ESONPointer,
|
start?: JSONPath,
|
||||||
end: ESONPointer
|
end?: JSONPath,
|
||||||
|
before?: JSONPath,
|
||||||
|
after?: JSONPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: ESONPointer.path is an array, JSONSchemaError.path is a string -> make this consistent
|
// TODO: ESONPointer.path is an array, JSONSchemaError.path is a string -> make this consistent
|
||||||
|
|
Loading…
Reference in New Issue