diff --git a/src/actions.js b/src/actions.js index cc3813f..be419a4 100644 --- a/src/actions.js +++ b/src/actions.js @@ -92,17 +92,18 @@ export function changeType (data, path, type) { * and object property * * @param {ESON} data - * @param {ESONSelection} selection + * @param {Selection} selection * @return {Array} */ export function duplicate (data, selection) { // console.log('duplicate', path) + if (!selection.start || !selection.end) { + return [] + } 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 { maxIndex } = findSelectionIndices(root, start, end) + const { maxIndex } = findSelectionIndices(root, rootPath, selection) const paths = pathsFromSelection(data, selection) if (root.type === 'Array') { @@ -113,7 +114,6 @@ export function duplicate (data, selection) { })) } else { // object.type === 'Object' - const { maxIndex } = findSelectionIndices(root, start, end) const nextProp = root.props && root.props[maxIndex] 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. * @@ -233,17 +186,14 @@ export function insertBefore (data, path, values) { // TODO: find a better name * and object property * * @param {ESON} data - * @param {ESONSelection} selection + * @param {Selection} selection * @param {Array.<{name?: string, value: JSONType, type?: ESONType}>} values * @return {Array} */ export function replace (data, selection, values) { // TODO: find a better name and define datastructure for values - 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 { minIndex, maxIndex } = findSelectionIndices(root, start, end) + const { minIndex, maxIndex } = findSelectionIndices(root, rootPath, selection) if (root.type === 'Array') { const removeActions = removeAll(pathsFromSelection(data, selection)) diff --git a/src/components/JSONNode.js b/src/components/JSONNode.js index a825ba3..c2389fb 100644 --- a/src/components/JSONNode.js +++ b/src/components/JSONNode.js @@ -16,15 +16,15 @@ import type { ESONObjectProperty, ESON, SearchResultStatus, Path } from '../type const SELECTED_CLASS_NAMES = { [SELECTED]: ' jsoneditor-selected', [SELECTED_END]: ' jsoneditor-selected jsoneditor-selected-end', - [SELECTED_AFTER]: ' jsoneditor-selected jsoneditor-selected-after', - [SELECTED_BEFORE]: ' jsoneditor-selected jsoneditor-selected-before', + [SELECTED_AFTER]: ' jsoneditor-selected jsoneditor-selected-insert-area', + [SELECTED_BEFORE]: ' jsoneditor-selected jsoneditor-selected-insert-area', } const HOVERED_CLASS_NAMES = { [SELECTED]: ' jsoneditor-hover', [SELECTED_END]: ' jsoneditor-hover jsoneditor-hover-end', - [SELECTED_AFTER]: ' jsoneditor-hover jsoneditor-hover-after', - [SELECTED_BEFORE]: ' jsoneditor-hover jsoneditor-hover-before', + [SELECTED_AFTER]: ' jsoneditor-hover jsoneditor-hover-insert-area', + [SELECTED_BEFORE]: ' jsoneditor-hover jsoneditor-hover-insert-area', } export default class JSONNode extends PureComponent { @@ -196,7 +196,7 @@ export default class JSONNode extends PureComponent { ]) : null - const insertArea = this.renderInsertArea() + const insertArea = this.renderInsertBeforeArea() return h('div', { 'data-path': compileJSONPointer(this.props.path), @@ -206,8 +206,8 @@ export default class JSONNode extends PureComponent { }, [node, floatingMenu, insertArea]) } - renderInsertArea () { - const floatingMenu = (this.props.data.selected === SELECTED_AFTER) + renderInsertBeforeArea () { + const floatingMenu = (this.props.data.selected === SELECTED_BEFORE) ? this.renderFloatingMenu([ {type: 'insertStructure'}, {type: 'insertValue'}, @@ -220,7 +220,7 @@ export default class JSONNode extends PureComponent { return h('div', { key: 'menu', className: 'jsoneditor-insert-area', - 'data-area': 'after' + 'data-area': 'before' }, [floatingMenu]) } diff --git a/src/components/TreeMode.js b/src/components/TreeMode.js index 3e2ee21..1170428 100644 --- a/src/components/TreeMode.js +++ b/src/components/TreeMode.js @@ -37,7 +37,7 @@ import { import { createFindKeyBinding } from '../utils/keyBindings' 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 = { allErrors: true, @@ -348,17 +348,21 @@ export default class TreeMode extends Component { } 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 // apply focus to new node - this.focusToNext(path) + this.focusToPrevious(path) } handleInsertStructure = (path) => { // TODO: implement handleInsertStructure - console.log('handleInsertStructure', path) + console.log('TODO: handleInsertStructure', path) alert('not yet implemented...') } @@ -456,7 +460,7 @@ export default class TreeMode extends Component { handleKeyDownDuplicate = (event) => { const path = this.findDataPathFromElement(event.target) if (path) { - const selection = { start: {path}, end: {path} } + const selection = { start: path, end: path } this.handlePatch(duplicate(this.state.data, selection)) // 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 */ focusToNext (path) { - // apply focus to new element setTimeout(() => { const element = findNode(this.refs.contents, path) 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) => { this.handlePatch(sort(this.state.data, path, order)) } - handleSelect = (selection: ESONSelection) => { - console.log('handleSelect', selection) + handleSelect = (selection: Selection) => { this.setState({ selection }) } @@ -664,7 +679,7 @@ export default class TreeMode extends Component { (event.target.contentEditable !== 'true') if (clickedOnEmptySpace && pointer) { - this.setState({ selection: {start: pointer, end: pointer}}) + this.setState({ selection: this.selectionFromESONPointer(pointer)}) } else { this.setState({ selection: null }) @@ -672,12 +687,13 @@ export default class TreeMode extends Component { } handlePan = (event) => { + const selection = this.state.selection 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({ selection: { - start: this.state.selection.start, - end: {path} + start: selection.start || selection.before || selection.after, + end: path } }) } @@ -711,6 +727,18 @@ export default class TreeMode extends Component { 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 * @param {Path} path @@ -770,7 +798,6 @@ export default class TreeMode extends Component { } undo = () => { - console.log('undo') if (this.canUndo()) { const history = this.state.history const historyIndex = this.state.historyIndex diff --git a/src/develop.html b/src/develop.html index 8f1abbc..dc3a1da 100644 --- a/src/develop.html +++ b/src/develop.html @@ -12,6 +12,10 @@