diff --git a/README.md b/README.md index ebcabad4..272df668 100644 --- a/README.md +++ b/README.md @@ -232,7 +232,7 @@ Pass the JSON contents to be rendered in the JSONEditor. `Content` is an object selection: JSONEditorSelection | undefined ``` -The current selected contents. You can use two-way binding using `bind:selection`. The `tree` mode supports `MultiSelection`, `KeySelection`, `ValueSelection`, `InsideSelection`, or `AfterSelection`. The `table` mode supports `ValueSelection`, and `text` mode supports `TextSelection.`. +The current selected contents. You can use two-way binding using `bind:selection`. The `tree` mode supports `MultiSelection`, `KeySelection`, `EditKeySelection`, `ValueSelection`, `EditValueSelection`, `InsideSelection`, or `AfterSelection`. The `table` mode supports `ValueSelection`, and `text` mode supports `TextSelection.`. #### mode @@ -586,7 +586,7 @@ A menu item `ContextMenuItem` can be one of the following types: onSelect: (selection: JSONEditorSelection | undefined) => void ``` -Callback invoked when the selection is changed. When the selection is removed, the callback is invoked with `undefined` as argument. In `text` mode, a `TextSelection` will be fired. In `tree` and `table` mode, a `JSONSelection` will be fired (which can be `MultiSelection`, `KeySelection`, `ValueSelection`, `InsideSelection`, or `AfterSelection`). Use typeguards like `isTextSelection` and `isValueSelection` to check what type the selection has. +Callback invoked when the selection is changed. When the selection is removed, the callback is invoked with `undefined` as argument. In `text` mode, a `TextSelection` will be fired. In `tree` and `table` mode, a `JSONSelection` will be fired (which can be `MultiSelection`, `KeySelection`, `EditKeySelection`, `ValueSelection`, `EditValueSelection`, `InsideSelection`, or `AfterSelection`). Use typeguards like `isTextSelection` and `isValueSelection` to check what type the selection has. #### queryLanguages @@ -856,7 +856,9 @@ The library exports a set of utility functions. The exact definitions of those f - `isMultiSelection`, - `isEditingSelection` - `createValueSelection` + - `createEditValueSelection` - `createKeySelection` + - `createEditKeySelection` - `createInsideSelection`, - `createAfterSelection` - `createMultiSelection` diff --git a/src/lib/components/controls/EditableDiv.svelte b/src/lib/components/controls/EditableDiv.svelte index 88ac4ca1..791a616f 100644 --- a/src/lib/components/controls/EditableDiv.svelte +++ b/src/lib/components/controls/EditableDiv.svelte @@ -13,6 +13,7 @@ const debug = createDebug('jsoneditor:EditableDiv') export let value: string + export let initialValue: string | undefined export let shortText = false export let label: string export let onChange: (newValue: string, updateSelection: UpdateSelectionAfterChange) => void @@ -27,17 +28,12 @@ let closed = false onMount(() => { - debug('onMount', { value }) - setDomValue(value) + debug('onMount', { value, initialValue }) + setDomValue(initialValue !== undefined ? initialValue : value) // focus if (domValue) { setCursorToEnd(domValue) - - // The refresh method can be used to update the classnames for example - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - domValue.refresh = handleValueInput } }) diff --git a/src/lib/components/modes/tablemode/TableMode.svelte b/src/lib/components/modes/tablemode/TableMode.svelte index ae7aae26..3037e6d4 100644 --- a/src/lib/components/modes/tablemode/TableMode.svelte +++ b/src/lib/components/modes/tablemode/TableMode.svelte @@ -673,9 +673,7 @@ const index = parseInt(path[0], 10) const nextPath = [String(index + 1), ...path.slice(1)] - return existsIn(json, nextPath) - ? createValueSelection(nextPath, false) - : createValueSelection(path, false) + return existsIn(json, nextPath) ? createValueSelection(nextPath) : createValueSelection(path) } export function focus() { @@ -736,7 +734,7 @@ return } - updateSelection(createValueSelection(path, false)) + updateSelection(createValueSelection(path)) event.preventDefault() } @@ -752,7 +750,7 @@ // Select the first row, first column const path = ['0', ...columns[0]] - return createValueSelection(path, false) + return createValueSelection(path) } else { return undefined } @@ -1047,7 +1045,7 @@ if (isObjectOrArray(value)) { openJSONEditorModal(path) } else { - updateSelection(createValueSelection(path, true)) + updateSelection(createValueSelection(path)) } } @@ -1173,7 +1171,6 @@ await onInsertCharacter({ char, selectInside: false, - refJsonEditor, json, selection: selection, readOnly, @@ -1453,7 +1450,7 @@ function handleSelectValidationError(error: ValidationError) { debug('select validation error', error) - updateSelection(createValueSelection(error.path, false)) + updateSelection(createValueSelection(error.path)) scrollTo(error.path) } diff --git a/src/lib/components/modes/treemode/JSONKey.svelte b/src/lib/components/modes/treemode/JSONKey.svelte index 2996be94..db393d43 100644 --- a/src/lib/components/modes/treemode/JSONKey.svelte +++ b/src/lib/components/modes/treemode/JSONKey.svelte @@ -3,6 +3,7 @@ @@ -70,6 +71,7 @@ {#if !context.readOnly && isEditingKey} ({ // expand the newly replaced array and select it state: expandSmart(patchedJson, patchedState, rootPath), - selection: createValueSelection(rootPath, false) + selection: createValueSelection(rootPath) })) }, onClose: () => { @@ -1108,7 +1108,7 @@ handlePatch(operations, (patchedJson, patchedState) => ({ // expand the newly replaced array and select it state: expandSmart(patchedJson, patchedState, rootPath), - selection: createValueSelection(rootPath, false) + selection: createValueSelection(rootPath) })) } }, @@ -1514,7 +1514,7 @@ const parent = getIn(json, initial(path)) if (Array.isArray(parent)) { // change into selection of the value - updateSelection(createValueSelection(path, false)) + updateSelection(createValueSelection(path)) } } diff --git a/src/lib/index.ts b/src/lib/index.ts index 2b145d2a..e9bfab0d 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -50,7 +50,9 @@ export { isMultiSelection, isEditingSelection, createValueSelection, + createEditValueSelection, createKeySelection, + createEditKeySelection, createInsideSelection, createAfterSelection, createMultiSelection, diff --git a/src/lib/logic/actions.ts b/src/lib/logic/actions.ts index 8217a017..a5f6962a 100644 --- a/src/lib/logic/actions.ts +++ b/src/lib/logic/actions.ts @@ -1,11 +1,11 @@ import { + createEditKeySelection, + createEditValueSelection, createInsideSelection, - createKeySelection, createMultiSelection, createValueSelection, getFocusPath, hasSelectionContents, - isEditingSelection, isKeySelection, isValueSelection, selectionToPartialJson @@ -40,9 +40,8 @@ import { parsePath } from 'immutable-json-patch' import { isObject, isObjectOrArray } from '$lib/utils/typeUtils.js' -import { expandAll, expandSmart, expandPath, expandNone } from '$lib/logic/documentState.js' +import { expandAll, expandNone, expandPath, expandSmart } from '$lib/logic/documentState.js' import { initial, isEmpty, last } from 'lodash-es' -import { insertActiveElementContents } from '$lib/utils/domUtils.js' import { fromTableCellPosition, toTableCellPosition } from '$lib/logic/table.js' const debug = createDebug('jsoneditor:actions') @@ -135,7 +134,7 @@ export function onPaste({ function doPaste(pastedText: string) { if (json !== undefined) { - const ensureSelection = selection || createValueSelection([], false) + const ensureSelection = selection || createValueSelection([]) const operations = insert(json, ensureSelection, pastedText, parser) @@ -281,7 +280,7 @@ export function onDuplicateRow({ onPatch(operations, (_, patchedState) => { const newRowIndex = rowIndex < (json as Array).length ? rowIndex + 1 : rowIndex const newPath = fromTableCellPosition({ rowIndex: newRowIndex, columnIndex }, columns) - const newSelection = createValueSelection(newPath, false) + const newSelection = createValueSelection(newPath) return { state: patchedState, @@ -366,7 +365,7 @@ export function onInsertAfterRow({ onPatch(operations, (_, patchedState) => { const nextPath = fromTableCellPosition({ rowIndex: nextRowIndex, columnIndex }, columns) - const newSelection = createValueSelection(nextPath, false) + const newSelection = createValueSelection(nextPath) return { state: patchedState, @@ -411,8 +410,7 @@ export function onRemoveRow({ json, selection, columns, readOnly, onPatch }: OnR const newSelection = newRowIndex !== undefined ? createValueSelection( - fromTableCellPosition({ rowIndex: newRowIndex, columnIndex }, columns), - false + fromTableCellPosition({ rowIndex: newRowIndex, columnIndex }, columns) ) : undefined @@ -428,9 +426,9 @@ export function onRemoveRow({ json, selection, columns, readOnly, onPatch }: OnR export interface OnInsert { insertType: InsertType selectInside: boolean - refJsonEditor: HTMLElement json: unknown | undefined selection: JSONSelection | undefined + initialValue: string | undefined readOnly: boolean parser: JSONParser onPatch: OnPatch @@ -441,7 +439,7 @@ export interface OnInsert { export function onInsert({ insertType, selectInside, - refJsonEditor, + initialValue, json, selection, readOnly, @@ -483,8 +481,8 @@ export function onInsert({ return { state: expandPath(patchedJson, patchedState, path, expandNone), selection: isObject(parent) - ? createKeySelection(path, true) - : createValueSelection(path, true) + ? createEditKeySelection(path, initialValue) + : createEditValueSelection(path, initialValue) } } } @@ -493,13 +491,6 @@ export function onInsert({ }) debug('after patch') - - if (operation) { - if (newValue === '') { - // open the newly inserted value in edit mode (can be cancelled via ESC this way) - tick2(() => insertActiveElementContents(refJsonEditor, '', true, refreshEditableDiv)) - } - } } else { // document is empty or invalid (in that case it has text but no json) debug('onInsert', { insertType, newValue }) @@ -509,7 +500,7 @@ export function onInsert({ state: expandSmart(patchedJson, patchedState, path), selection: isObjectOrArray(newValue) ? createInsideSelection(path) - : createValueSelection(path, true) + : createEditValueSelection(path) })) } } @@ -517,7 +508,6 @@ export function onInsert({ export interface OnInsertCharacter { char: string selectInside: boolean - refJsonEditor: HTMLElement json: unknown | undefined selection: JSONSelection | undefined readOnly: boolean @@ -531,7 +521,6 @@ export interface OnInsertCharacter { export async function onInsertCharacter({ char, selectInside, - refJsonEditor, json, selection, readOnly, @@ -547,15 +536,7 @@ export async function onInsertCharacter({ } if (isKeySelection(selection)) { - // only replace contents when not yet in edit mode (can happen when entering - // multiple characters very quickly after each other due to the async handling) - const replaceContents = !selection.edit - - onSelect({ ...selection, edit: true }) - tick2(() => - // We use this way via insertActiveElementContents, so we can cancel via ESC - insertActiveElementContents(refJsonEditor, char, replaceContents, refreshEditableDiv) - ) + onSelect({ ...selection, edit: true, initialValue: char }) return } @@ -563,7 +544,7 @@ export async function onInsertCharacter({ onInsert({ insertType: 'object', selectInside, - refJsonEditor, + initialValue: undefined, // not relevant json, selection, readOnly, @@ -575,7 +556,7 @@ export async function onInsertCharacter({ onInsert({ insertType: 'array', selectInside, - refJsonEditor, + initialValue: undefined, // not relevant json, selection, readOnly, @@ -586,15 +567,7 @@ export async function onInsertCharacter({ } else { if (isValueSelection(selection) && json !== undefined) { if (!isObjectOrArray(getIn(json, selection.path))) { - // only replace contents when not yet in edit mode (can happen when entering - // multiple characters very quickly after each other due to the async handling) - const replaceContents = !selection.edit - - onSelect({ ...selection, edit: true }) - tick2(() => - // We use this way via insertActiveElementContents, so we can cancel via ESC - insertActiveElementContents(refJsonEditor, char, replaceContents, refreshEditableDiv) - ) + onSelect({ ...selection, edit: true, initialValue: char }) } else { // TODO: replace the object/array with editing a text in edit mode? // (Ideally this this should not create an entry in history though, @@ -605,7 +578,6 @@ export async function onInsertCharacter({ debug('onInsertValueWithCharacter', { char }) await onInsertValueWithCharacter({ char, - refJsonEditor, json, selection, readOnly, @@ -619,7 +591,6 @@ export async function onInsertCharacter({ interface OnInsertValueWithCharacter { char: string - refJsonEditor: HTMLElement json: unknown | undefined selection: JSONSelection | undefined readOnly: boolean @@ -630,7 +601,6 @@ interface OnInsertValueWithCharacter { async function onInsertValueWithCharacter({ char, - refJsonEditor, json, selection, readOnly, @@ -646,7 +616,7 @@ async function onInsertValueWithCharacter({ onInsert({ insertType: 'value', selectInside: false, // not relevant, we insert a value, not an object or array - refJsonEditor, + initialValue: char, json, selection, readOnly, @@ -654,29 +624,4 @@ async function onInsertValueWithCharacter({ onPatch, onReplaceJson }) - - // only replace contents when not yet in edit mode (can happen when entering - // multiple characters very quickly after each other due to the async handling) - const replaceContents = !isEditingSelection(selection) - - tick2(() => insertActiveElementContents(refJsonEditor, char, replaceContents, refreshEditableDiv)) -} - -/** - * set two timeouts, two ticks of delay. - * This allows to perform some action in the DOM *after* Svelte has re-rendered the app for example - * WARNING: try to avoid using this function, it is tricky to rely on it. - */ -function tick2(callback: () => void) { - setTimeout(() => setTimeout(callback)) -} - -function refreshEditableDiv(element: HTMLElement) { - // We force a refresh because when changing the text of the editable div programmatically, - // the DIV doesn't get a trigger to update it's class - // TODO: come up with a better solution - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - element?.refresh() } diff --git a/src/lib/logic/operations.ts b/src/lib/logic/operations.ts index 678b88f8..7e450585 100644 --- a/src/lib/logic/operations.ts +++ b/src/lib/logic/operations.ts @@ -678,7 +678,7 @@ export function createRemoveOperations( // there is no parent, this is the root document const operations: JSONPatchDocument = [{ op: 'replace', path: '', value: '' }] - const newSelection = createValueSelection([], false) + const newSelection = createValueSelection([]) return { operations, newSelection } } diff --git a/src/lib/logic/search.test.ts b/src/lib/logic/search.test.ts index 8b588c9b..b0362b55 100644 --- a/src/lib/logic/search.test.ts +++ b/src/lib/logic/search.test.ts @@ -331,7 +331,7 @@ describe('search', () => { } ]) - assert.deepStrictEqual(newSelection, createValueSelection(['hello world'], false)) + assert.deepStrictEqual(newSelection, createValueSelection(['hello world'])) const updatedJson = immutableJSONPatch(json, operations) assert.deepStrictEqual(updatedJson, { @@ -364,7 +364,7 @@ describe('search', () => { { op: 'move', from: '/after', path: '/after' } ]) - assert.deepStrictEqual(newSelection, createKeySelection(['hello *'], false)) + assert.deepStrictEqual(newSelection, createKeySelection(['hello *'])) const updatedJson = immutableJSONPatch(json, operations) assert.deepStrictEqual(updatedJson, { @@ -527,7 +527,7 @@ describe('search', () => { { op: 'move', from: '/after', path: '/after' } ]) - assert.deepStrictEqual(newSelection, createKeySelection(['hello *'], false)) + assert.deepStrictEqual(newSelection, createKeySelection(['hello *'])) const updatedJson = immutableJSONPatch(json, operations) assert.deepStrictEqual(updatedJson, { diff --git a/src/lib/logic/selection.test.ts b/src/lib/logic/selection.test.ts index 2b38e5ad..3ff84c54 100644 --- a/src/lib/logic/selection.test.ts +++ b/src/lib/logic/selection.test.ts @@ -80,14 +80,14 @@ describe('selection', () => { test('should expand a key selection', () => { const path = ['obj', 'arr'] - const actual = getSelectionPaths(json, createKeySelection(path, false)) + const actual = getSelectionPaths(json, createKeySelection(path)) assert.deepStrictEqual(actual, [['obj', 'arr']]) }) test('should expand a value selection', () => { const path = ['obj', 'arr'] - const actual = getSelectionPaths(json, createValueSelection(path, false)) + const actual = getSelectionPaths(json, createValueSelection(path)) assert.deepStrictEqual(actual, [['obj', 'arr']]) }) @@ -142,10 +142,10 @@ describe('selection', () => { const path2 = ['a'] assert.deepStrictEqual(findRootPath(json, createAfterSelection(path1)), path2) assert.deepStrictEqual(findRootPath(json, createInsideSelection(path1)), path2) - assert.deepStrictEqual(findRootPath(json, createKeySelection(path1, false)), path2) - assert.deepStrictEqual(findRootPath(json, createValueSelection(path1, false)), path2) - assert.deepStrictEqual(findRootPath(json, createValueSelection(path2, false)), path2) - assert.deepStrictEqual(findRootPath(json, createKeySelection(path2, false)), path2) + assert.deepStrictEqual(findRootPath(json, createKeySelection(path1)), path2) + assert.deepStrictEqual(findRootPath(json, createValueSelection(path1)), path2) + assert.deepStrictEqual(findRootPath(json, createValueSelection(path2)), path2) + assert.deepStrictEqual(findRootPath(json, createKeySelection(path2)), path2) assert.deepStrictEqual(findRootPath(json, createMultiSelection(['a'], ['a'])), path2) }) @@ -162,28 +162,28 @@ describe('selection', () => { test('getSelectionLeft', () => { assert.deepStrictEqual( - getSelectionLeft(json2, documentState2, createValueSelection(['path'], false)), - createKeySelection(['path'], false) + getSelectionLeft(json2, documentState2, createValueSelection(['path'])), + createKeySelection(['path']) ) assert.deepStrictEqual( - getSelectionLeft(json2, documentState2, createKeySelection(['path1'], false)), + getSelectionLeft(json2, documentState2, createKeySelection(['path1'])), createAfterSelection(['path']) ) assert.deepStrictEqual( getSelectionLeft(json2, documentState2, createAfterSelection(['path'])), - createValueSelection(['path'], false) + createValueSelection(['path']) ) assert.deepStrictEqual( getSelectionLeft(json2, documentState2, createInsideSelection([])), - createValueSelection([], false) + createValueSelection([]) ) assert.deepStrictEqual( getSelectionLeft(json2, documentState2, createMultiSelection(['path1'], ['path2'])), - createKeySelection(['path2'], false) + createKeySelection(['path2']) ) }) @@ -193,7 +193,7 @@ describe('selection', () => { json: json2, expand: () => false }) - const selection = createValueSelection(['1'], false) + const selection = createValueSelection(['1']) assert.deepStrictEqual( getSelectionLeft(json2, documentState2, selection), @@ -204,12 +204,7 @@ describe('selection', () => { test('getSelectionLeft: keep anchor path', () => { const keepAnchorPath = true assert.deepStrictEqual( - getSelectionLeft( - json2, - documentState2, - createValueSelection(['path'], false), - keepAnchorPath - ), + getSelectionLeft(json2, documentState2, createValueSelection(['path']), keepAnchorPath), { type: SelectionType.multi, anchorPath: ['path'], @@ -220,23 +215,23 @@ describe('selection', () => { test('getSelectionRight', () => { assert.deepStrictEqual( - getSelectionRight(json2, documentState2, createKeySelection(['path'], false)), - createValueSelection(['path'], false) + getSelectionRight(json2, documentState2, createKeySelection(['path'])), + createValueSelection(['path']) ) assert.deepStrictEqual( - getSelectionRight(json2, documentState2, createValueSelection([], false)), + getSelectionRight(json2, documentState2, createValueSelection([])), createInsideSelection([]) ) assert.deepStrictEqual( - getSelectionRight(json2, documentState2, createValueSelection(['path'], false)), + getSelectionRight(json2, documentState2, createValueSelection(['path'])), createAfterSelection(['path']) ) assert.deepStrictEqual( getSelectionRight(json2, documentState2, createAfterSelection(['path'])), - createKeySelection(['path1'], false) + createKeySelection(['path1']) ) assert.deepStrictEqual( @@ -246,19 +241,14 @@ describe('selection', () => { assert.deepStrictEqual( getSelectionRight(json2, documentState2, createMultiSelection(['path1'], ['path2'])), - createValueSelection(['path2'], false) + createValueSelection(['path2']) ) }) test('getSelectionRight: keep anchor path', () => { const keepAnchorPath = true assert.deepStrictEqual( - getSelectionRight( - json2, - documentState2, - createKeySelection(['path'], false), - keepAnchorPath - ), + getSelectionRight(json2, documentState2, createKeySelection(['path']), keepAnchorPath), { type: SelectionType.multi, anchorPath: ['path'], @@ -280,78 +270,78 @@ describe('selection', () => { test('should get selection up from KEY selection', () => { assert.deepStrictEqual( - getSelectionUp(json2, documentState2, createKeySelection(['obj'], false)), - createKeySelection(['a'], false) + getSelectionUp(json2, documentState2, createKeySelection(['obj'])), + createKeySelection(['a']) ) assert.deepStrictEqual( - getSelectionUp(json2, documentState2, createKeySelection(['obj', 'c'], false)), - createKeySelection(['obj'], false) + getSelectionUp(json2, documentState2, createKeySelection(['obj', 'c'])), + createKeySelection(['obj']) ) // jump from key to array value assert.deepStrictEqual( - getSelectionUp(json2, documentState2, createKeySelection(['d'], false)), - createValueSelection(['arr', '1'], false) + getSelectionUp(json2, documentState2, createKeySelection(['d'])), + createValueSelection(['arr', '1']) ) }) test('should get selection up from VALUE selection', () => { assert.deepStrictEqual( - getSelectionUp(json2, documentState2, createValueSelection(['obj'], false)), - createValueSelection(['a'], false) + getSelectionUp(json2, documentState2, createValueSelection(['obj'])), + createValueSelection(['a']) ) assert.deepStrictEqual( - getSelectionUp(json2, documentState2, createValueSelection(['obj', 'c'], false)), - createValueSelection(['obj'], false) + getSelectionUp(json2, documentState2, createValueSelection(['obj', 'c'])), + createValueSelection(['obj']) ) assert.deepStrictEqual( - getSelectionUp(json2, documentState2, createValueSelection(['d'], false)), - createValueSelection(['arr', '1'], false) + getSelectionUp(json2, documentState2, createValueSelection(['d'])), + createValueSelection(['arr', '1']) ) assert.deepStrictEqual( - getSelectionUp(json2, documentState2, createValueSelection(['arr', '1'], false)), - createValueSelection(['arr', '0'], false) + getSelectionUp(json2, documentState2, createValueSelection(['arr', '1'])), + createValueSelection(['arr', '0']) ) }) test('should get selection up from AFTER selection', () => { assert.deepStrictEqual( getSelectionUp(json2, documentState2, createAfterSelection(['arr', '1'])), - createValueSelection(['arr', '1'], false) + createValueSelection(['arr', '1']) ) // FIXME: this should return a value selection of /obj/c instead of /obj assert.deepStrictEqual( getSelectionUp(json2, documentState2, createAfterSelection(['obj'])), - createValueSelection(['obj'], false) + createValueSelection(['obj']) ) }) test('should get selection up from INSIDE selection', () => { assert.deepStrictEqual( getSelectionUp(json2, documentState2, createInsideSelection(['arr'])), - createValueSelection(['arr'], false) + createValueSelection(['arr']) ) assert.deepStrictEqual( getSelectionUp(json2, documentState2, createInsideSelection(['obj'])), - createValueSelection(['obj'], false) + createValueSelection(['obj']) ) assert.deepStrictEqual( getSelectionUp(json2, documentState2, createInsideSelection([])), - createValueSelection([], false) + createValueSelection([]) ) }) test('should get selection up from MULTI selection', () => { assert.deepStrictEqual( getSelectionUp(json2, documentState2, createMultiSelection(['d'], ['obj'])), - createValueSelection(['a'], false) + createValueSelection(['a']) ) assert.deepStrictEqual( @@ -361,12 +351,12 @@ describe('selection', () => { assert.deepStrictEqual( getSelectionUp(json2, documentState2, createMultiSelection(['obj'], ['d'])), - createValueSelection(['a'], false) + createValueSelection(['a']) ) assert.deepStrictEqual( - getSelectionUp(json2, documentState2, createMultiSelection(['obj'], ['d']), false), - createValueSelection(['a'], false) + getSelectionUp(json2, documentState2, createMultiSelection(['obj'], ['d'])), + createValueSelection(['a']) ) assert.deepStrictEqual( @@ -375,8 +365,8 @@ describe('selection', () => { ) assert.deepStrictEqual( - getSelectionUp(json2, documentState2, createMultiSelection(['a'], ['a']), false), - createValueSelection([], false) + getSelectionUp(json2, documentState2, createMultiSelection(['a'], ['a'])), + createValueSelection([]) ) }) }) @@ -394,70 +384,70 @@ describe('selection', () => { test('should get selection down from KEY selection', () => { assert.deepStrictEqual( - getSelectionDown(json2, documentState2, createKeySelection(['obj'], false)), - createKeySelection(['obj', 'c'], false) + getSelectionDown(json2, documentState2, createKeySelection(['obj'])), + createKeySelection(['obj', 'c']) ) assert.deepStrictEqual( - getSelectionDown(json2, documentState2, createKeySelection(['obj', 'c'], false)), - createKeySelection(['arr'], false) + getSelectionDown(json2, documentState2, createKeySelection(['obj', 'c'])), + createKeySelection(['arr']) ) // jump from key to array value assert.deepStrictEqual( - getSelectionDown(json2, documentState2, createKeySelection(['arr'], false)), - createValueSelection(['arr', '0'], false) + getSelectionDown(json2, documentState2, createKeySelection(['arr'])), + createValueSelection(['arr', '0']) ) }) test('should get selection down from VALUE selection', () => { assert.deepStrictEqual( - getSelectionDown(json2, documentState2, createValueSelection(['obj'], false)), - createValueSelection(['obj', 'c'], false) + getSelectionDown(json2, documentState2, createValueSelection(['obj'])), + createValueSelection(['obj', 'c']) ) assert.deepStrictEqual( - getSelectionDown(json2, documentState2, createValueSelection(['obj', 'c'], false)), - createValueSelection(['arr'], false) + getSelectionDown(json2, documentState2, createValueSelection(['obj', 'c'])), + createValueSelection(['arr']) ) assert.deepStrictEqual( - getSelectionDown(json2, documentState2, createValueSelection(['arr', '1'], false)), - createValueSelection(['d'], false) + getSelectionDown(json2, documentState2, createValueSelection(['arr', '1'])), + createValueSelection(['d']) ) assert.deepStrictEqual( - getSelectionDown(json2, documentState2, createValueSelection(['arr', '0'], false)), - createValueSelection(['arr', '1'], false) + getSelectionDown(json2, documentState2, createValueSelection(['arr', '0'])), + createValueSelection(['arr', '1']) ) }) test('should get selection down from AFTER selection', () => { assert.deepStrictEqual( getSelectionDown(json2, documentState2, createAfterSelection(['arr', '0'])), - createValueSelection(['arr', '1'], false) + createValueSelection(['arr', '1']) ) assert.deepStrictEqual( getSelectionDown(json2, documentState2, createAfterSelection(['arr', '1'])), - createValueSelection(['d'], false) + createValueSelection(['d']) ) assert.deepStrictEqual( getSelectionDown(json2, documentState2, createAfterSelection(['obj'])), - createValueSelection(['arr'], false) + createValueSelection(['arr']) ) }) test('should get selection down from INSIDE selection', () => { assert.deepStrictEqual( getSelectionDown(json2, documentState2, createInsideSelection(['arr'])), - createValueSelection(['arr', '0'], false) + createValueSelection(['arr', '0']) ) assert.deepStrictEqual( getSelectionDown(json2, documentState2, createInsideSelection(['obj'])), - createValueSelection(['obj', 'c'], false) + createValueSelection(['obj', 'c']) ) assert.deepStrictEqual( @@ -469,12 +459,12 @@ describe('selection', () => { test('should get selection down from MULTI selection', () => { assert.deepStrictEqual( getSelectionDown(json2, documentState2, createMultiSelection(['arr'], ['a'])), - createValueSelection(['d'], false) + createValueSelection(['d']) ) assert.deepStrictEqual( - getSelectionDown(json2, documentState2, createMultiSelection(['arr'], ['a']), false), - createValueSelection(['d'], false) + getSelectionDown(json2, documentState2, createMultiSelection(['arr'], ['a'])), + createValueSelection(['d']) ) assert.deepStrictEqual( @@ -483,8 +473,8 @@ describe('selection', () => { ) assert.deepStrictEqual( - getSelectionDown(json2, documentState2, createMultiSelection(['a'], ['arr']), false), - createValueSelection(['d'], false) + getSelectionDown(json2, documentState2, createMultiSelection(['a'], ['arr'])), + createValueSelection(['d']) ) assert.deepStrictEqual( @@ -493,8 +483,8 @@ describe('selection', () => { ) assert.deepStrictEqual( - getSelectionDown(json2, documentState2, createMultiSelection(['arr'], ['arr']), false), - createValueSelection(['d'], false) + getSelectionDown(json2, documentState2, createMultiSelection(['arr'], ['arr'])), + createValueSelection(['d']) ) }) @@ -509,7 +499,7 @@ describe('selection', () => { const documentState3 = createDocumentState({ json: json3, expand: () => true }) assert.deepStrictEqual( - getSelectionDown(json3, documentState3, createAfterSelection(['arr']), false), + getSelectionDown(json3, documentState3, createAfterSelection(['arr'])), undefined ) }) @@ -524,48 +514,42 @@ describe('selection', () => { assert.deepStrictEqual(getInitialSelectionWithState({}), { type: SelectionType.value, - path: [], - edit: false + path: [] }) assert.deepStrictEqual(getInitialSelectionWithState([]), { type: SelectionType.value, - path: [], - edit: false + path: [] }) assert.deepStrictEqual(getInitialSelectionWithState('test'), { type: SelectionType.value, - path: [], - edit: false + path: [] }) assert.deepStrictEqual(getInitialSelectionWithState({ a: 2, b: 3 }), { type: SelectionType.key, - path: ['a'], - edit: false + path: ['a'] }) assert.deepStrictEqual(getInitialSelectionWithState({ a: {} }), { type: SelectionType.key, - path: ['a'], - edit: false + path: ['a'] }) assert.deepStrictEqual(getInitialSelectionWithState([2, 3, 4]), { type: SelectionType.value, - path: ['0'], - edit: false + path: ['0'] }) }) test('should turn selection into text', () => { assert.deepStrictEqual( - selectionToPartialJson(json, createKeySelection(['str'], false), 2, JSON), + selectionToPartialJson(json, createKeySelection(['str']), 2, JSON), 'str' ) assert.deepStrictEqual( - selectionToPartialJson(json, createValueSelection(['str'], false), 2, JSON), + selectionToPartialJson(json, createValueSelection(['str']), 2, JSON), 'hello world' ) assert.deepStrictEqual( - selectionToPartialJson(json, createValueSelection(['obj', 'arr', '1'], false), 2, JSON), + selectionToPartialJson(json, createValueSelection(['obj', 'arr', '1']), 2, JSON), '2' ) assert.deepStrictEqual( @@ -602,7 +586,7 @@ describe('selection', () => { ) assert.deepStrictEqual( - selectionToPartialJson(json, createValueSelection(['obj'], false), 2, JSON), + selectionToPartialJson(json, createValueSelection(['obj']), 2, JSON), JSON.stringify(json.obj, null, 2) ) @@ -626,12 +610,7 @@ describe('selection', () => { const objArr2 = '{\n' + ' "first": 3,\n' + ' "last": 4\n' + '}' assert.deepStrictEqual( - selectionToPartialJson( - json, - createValueSelection(['obj', 'arr', '2'], false), - indentation, - JSON - ), + selectionToPartialJson(json, createValueSelection(['obj', 'arr', '2']), indentation, JSON), objArr2 ) assert.deepStrictEqual( @@ -698,7 +677,7 @@ describe('selection', () => { createSelectionFromOperations(json, [ { op: 'replace', path: '/str', value: 'hello world (updated)' } ]), - createValueSelection(['str'], false) + createValueSelection(['str']) ) }) @@ -709,14 +688,14 @@ describe('selection', () => { { op: 'move', from: '/foo', path: '/foo' }, { op: 'move', from: '/bar', path: '/bar' } ]), - createKeySelection(['strRenamed'], false) + createKeySelection(['strRenamed']) ) }) test('should get selection from renaming the last key of an object', () => { assert.deepStrictEqual( createSelectionFromOperations(json, [{ op: 'move', from: '/arr', path: '/arrRenamed' }]), - createKeySelection(['arrRenamed'], false) + createKeySelection(['arrRenamed']) ) }) @@ -730,7 +709,7 @@ describe('selection', () => { test('should get selection from inserting a new root document', () => { assert.deepStrictEqual( createSelectionFromOperations(json, [{ op: 'replace', path: '', value: 'test' }]), - createValueSelection([], false) + createValueSelection([]) ) }) }) @@ -763,7 +742,7 @@ describe('selection', () => { describe('selectionIfOverlapping', () => { test('should determine whether a KeySelection is relevant for given pointer', () => { - const selection = createKeySelection(['obj', 'arr'], false) + const selection = createKeySelection(['obj', 'arr']) assert.deepStrictEqual(selectionIfOverlapping(json, selection, []), selection) assert.deepStrictEqual(selectionIfOverlapping(json, selection, ['obj']), selection) @@ -775,7 +754,7 @@ describe('selection', () => { }) test('should determine whether a ValueSelection is relevant for given pointer', () => { - const selection = createValueSelection(['obj', 'arr'], false) + const selection = createValueSelection(['obj', 'arr']) assert.deepStrictEqual(selectionIfOverlapping(json, selection, []), selection) assert.deepStrictEqual(selectionIfOverlapping(json, selection, ['obj']), selection) @@ -844,14 +823,14 @@ describe('selection', () => { describe('pathInSelection', () => { test('should determine path in selection for a KeySelection', () => { - const selection = createKeySelection(['obj'], false) + const selection = createKeySelection(['obj']) assert.strictEqual(pathInSelection(json, selection, ['obj']), true) assert.strictEqual(pathInSelection(json, selection, ['str']), false) assert.strictEqual(pathInSelection(json, selection, ['obj', 'arr', '1']), false) }) test('should determine path in selection for a ValueSelection', () => { - const selection = createValueSelection(['obj'], false) + const selection = createValueSelection(['obj']) assert.strictEqual(pathInSelection(json, selection, ['obj']), true) assert.strictEqual(pathInSelection(json, selection, ['str']), false) assert.strictEqual(pathInSelection(json, selection, ['obj', 'arr', '1']), true) diff --git a/src/lib/logic/selection.ts b/src/lib/logic/selection.ts index 04407ffa..90aa533b 100644 --- a/src/lib/logic/selection.ts +++ b/src/lib/logic/selection.ts @@ -22,6 +22,8 @@ import type { AfterSelection, CaretPosition, DocumentState, + EditKeySelection, + EditValueSelection, InsideSelection, JSONEditorSelection, JSONParser, @@ -246,12 +248,12 @@ export function getSelectionUp( if (isAfterSelection(selection)) { // select the node itself, not the previous node, // FIXME: when after an expanded object/array, should go to the last item inside the object/array - return createValueSelection(focusPath, false) + return createValueSelection(focusPath) } if (isInsideSelection(selection)) { // select the node itself, not the previous node, - return createValueSelection(focusPath, false) + return createValueSelection(focusPath) } if (isKeySelection(selection)) { @@ -263,18 +265,18 @@ export function getSelectionUp( const parent = getIn(json, parentPath) if (Array.isArray(parent) || isEmpty(previousPath)) { // switch to value selection: array has no keys, and root object also not - return createValueSelection(previousPath, false) + return createValueSelection(previousPath) } else { - return createKeySelection(previousPath, false) + return createKeySelection(previousPath) } } if (isValueSelection(selection)) { - return previousPath !== undefined ? createValueSelection(previousPath, false) : undefined + return previousPath !== undefined ? createValueSelection(previousPath) : undefined } if (previousPath !== undefined) { - return createValueSelection(previousPath, false) + return createValueSelection(previousPath) } return undefined @@ -318,15 +320,15 @@ export function getSelectionDown( } if (isAfterSelection(selection)) { - return nextPathAfter !== undefined ? createValueSelection(nextPathAfter, false) : undefined + return nextPathAfter !== undefined ? createValueSelection(nextPathAfter) : undefined } if (isInsideSelection(selection)) { - return nextPath !== undefined ? createValueSelection(nextPath, false) : undefined + return nextPath !== undefined ? createValueSelection(nextPath) : undefined } if (isValueSelection(selection)) { - return nextPath !== undefined ? createValueSelection(nextPath, false) : undefined + return nextPath !== undefined ? createValueSelection(nextPath) : undefined } if (isKeySelection(selection)) { @@ -338,17 +340,17 @@ export function getSelectionDown( const parent = getIn(json, parentPath) if (Array.isArray(parent)) { // switch to value selection: array has no keys - return createValueSelection(nextPath, false) + return createValueSelection(nextPath) } else { - return createKeySelection(nextPath, false) + return createKeySelection(nextPath) } } if (isMultiSelection(selection)) { return nextPathAfter !== undefined - ? createValueSelection(nextPathAfter, false) + ? createValueSelection(nextPathAfter) : nextPath !== undefined - ? createValueSelection(nextPath, false) + ? createValueSelection(nextPath) : undefined } @@ -373,7 +375,7 @@ export function getSelectionNextInside( const nextPathInside = parent ? getNextVisiblePath(parent, documentState, childPath) : undefined if (nextPathInside) { - return createValueSelection(parentPath.concat(nextPathInside), false) + return createValueSelection(parentPath.concat(nextPathInside)) } else { return createAfterSelection(path) } @@ -449,7 +451,7 @@ export function getSelectionLeft( } if (isMultiSelection(selection) && !Array.isArray(parent)) { - return createKeySelection(selection.focusPath, false) + return createKeySelection(selection.focusPath) } return undefined @@ -481,7 +483,7 @@ export function getSelectionRight( } if (isMultiSelection(selection)) { - return createValueSelection(selection.focusPath, false) + return createValueSelection(selection.focusPath) } return undefined @@ -507,8 +509,8 @@ export function getInitialSelection( const path = visiblePaths[index] return path === undefined || path.length === 0 || Array.isArray(getIn(json, initial(path))) - ? createValueSelection(path, false) // Array items and root object/array do not have a key, so select value in that case - : createKeySelection(path, false) + ? createValueSelection(path) // Array items and root object/array do not have a key, so select value in that case + : createKeySelection(path) } export function createSelectionFromOperations( @@ -521,7 +523,7 @@ export function createSelectionFromOperations( // a replaced value const path = parsePath(json, operation.path) - return createValueSelection(path, false) + return createValueSelection(path) } } @@ -537,7 +539,7 @@ export function createSelectionFromOperations( // a renamed key const path = parsePath(json, firstOp.path) - return createKeySelection(path, false) + return createKeySelection(path) } } @@ -611,27 +613,31 @@ export function pathStartsWith(path: JSONPath, parentPath: JSONPath): boolean { export function removeEditModeFromSelection( selection: JSONSelection | undefined ): JSONSelection | undefined { - if ((isKeySelection(selection) || isValueSelection(selection)) && selection.edit) { - return { ...selection, edit: false } + if (isEditingSelection(selection)) { + const { type, path } = selection + return { type, path } as KeySelection | ValueSelection } return selection } -export function createKeySelection(path: JSONPath, edit: boolean): KeySelection { - return { - type: SelectionType.key, - path, - edit - } +export function createKeySelection(path: JSONPath): KeySelection { + return { type: SelectionType.key, path } } -export function createValueSelection(path: JSONPath, edit: boolean): ValueSelection { - return { - type: SelectionType.value, - path, - edit - } +export function createEditKeySelection(path: JSONPath, initialValue?: string): EditKeySelection { + return { type: SelectionType.key, path, edit: true, initialValue } +} + +export function createValueSelection(path: JSONPath): ValueSelection { + return { type: SelectionType.value, path } +} + +export function createEditValueSelection( + path: JSONPath, + initialValue?: string +): EditValueSelection { + return { type: SelectionType.value, path, edit: true, initialValue } } export function createInsideSelection(path: JSONPath): InsideSelection { @@ -715,8 +721,13 @@ export function selectionToPartialJson( return undefined } -export function isEditingSelection(selection: JSONSelection | undefined): boolean { - return (isKeySelection(selection) || isValueSelection(selection)) && selection.edit === true +export function isEditingSelection( + selection: JSONSelection | undefined +): selection is EditKeySelection | EditValueSelection { + return ( + (isKeySelection(selection) || isValueSelection(selection)) && + (selection as Record).edit === true + ) } /** @@ -724,7 +735,7 @@ export function isEditingSelection(selection: JSONSelection | undefined): boolea */ // TODO: write unit tests export function selectAll(): JSONSelection { - return createValueSelection([], false) + return createValueSelection([]) } // TODO: write unit tests @@ -749,9 +760,9 @@ export function canConvert(selection: JSONSelection | undefined): boolean { export function fromCaretPosition(caretPosition: CaretPosition): JSONSelection { switch (caretPosition.type) { case CaretType.key: - return createKeySelection(caretPosition.path, false) + return createKeySelection(caretPosition.path) case CaretType.value: - return createValueSelection(caretPosition.path, false) + return createValueSelection(caretPosition.path) case CaretType.after: return createAfterSelection(caretPosition.path) case CaretType.inside: @@ -764,9 +775,9 @@ export function fromCaretPosition(caretPosition: CaretPosition): JSONSelection { export function fromSelectionType(selectionType: SelectionType, path: JSONPath): JSONSelection { switch (selectionType) { case SelectionType.key: - return createKeySelection(path, false) + return createKeySelection(path) case SelectionType.value: - return createValueSelection(path, false) + return createValueSelection(path) case SelectionType.after: return createAfterSelection(path) case SelectionType.inside: diff --git a/src/lib/logic/table.test.ts b/src/lib/logic/table.test.ts index fa64812e..6cc83ca5 100644 --- a/src/lib/logic/table.test.ts +++ b/src/lib/logic/table.test.ts @@ -172,49 +172,49 @@ describe('table', () => { test('selectPreviousRow', () => { deepStrictEqual( - selectPreviousRow(columns, createValueSelection(['2', 'id'], false)), - createValueSelection(['1', 'id'], false) + selectPreviousRow(columns, createValueSelection(['2', 'id'])), + createValueSelection(['1', 'id']) ) deepStrictEqual( - selectPreviousRow(columns, createValueSelection(['0', 'id'], false)), - createValueSelection(['0', 'id'], false) + selectPreviousRow(columns, createValueSelection(['0', 'id'])), + createValueSelection(['0', 'id']) ) }) test('selectNextRow', () => { deepStrictEqual( - selectNextRow(json, columns, createValueSelection(['0', 'id'], false)), - createValueSelection(['1', 'id'], false) + selectNextRow(json, columns, createValueSelection(['0', 'id'])), + createValueSelection(['1', 'id']) ) deepStrictEqual( - selectNextRow(json, columns, createValueSelection(['1', 'id'], false)), - createValueSelection(['1', 'id'], false) + selectNextRow(json, columns, createValueSelection(['1', 'id'])), + createValueSelection(['1', 'id']) ) }) test('selectPreviousColumn', () => { deepStrictEqual( - selectPreviousColumn(columns, createValueSelection(['2', 'name'], false)), - createValueSelection(['2', 'id'], false) + selectPreviousColumn(columns, createValueSelection(['2', 'name'])), + createValueSelection(['2', 'id']) ) deepStrictEqual( - selectPreviousColumn(columns, createValueSelection(['2', 'id'], false)), - createValueSelection(['2', 'id'], false) + selectPreviousColumn(columns, createValueSelection(['2', 'id'])), + createValueSelection(['2', 'id']) ) }) test('selectNextColumn', () => { deepStrictEqual( - selectNextColumn(columns, createValueSelection(['2', 'id'], false)), - createValueSelection(['2', 'name'], false) + selectNextColumn(columns, createValueSelection(['2', 'id'])), + createValueSelection(['2', 'name']) ) deepStrictEqual( - selectNextColumn(columns, createValueSelection(['2', 'other'], false)), - createValueSelection(['2', 'other'], false) + selectNextColumn(columns, createValueSelection(['2', 'other'])), + createValueSelection(['2', 'other']) ) }) diff --git a/src/lib/logic/table.ts b/src/lib/logic/table.ts index b9c6aac8..0766ce93 100644 --- a/src/lib/logic/table.ts +++ b/src/lib/logic/table.ts @@ -269,7 +269,7 @@ export function selectPreviousRow(columns: JSONPath[], selection: JSONSelection) if (rowIndex > 0) { const previousPosition = { rowIndex: rowIndex - 1, columnIndex } const previousPath = fromTableCellPosition(previousPosition, columns) - return createValueSelection(previousPath, false) + return createValueSelection(previousPath) } return selection @@ -285,7 +285,7 @@ export function selectNextRow( if (rowIndex < (json as Array).length - 1) { const nextPosition = { rowIndex: rowIndex + 1, columnIndex } const nextPath = fromTableCellPosition(nextPosition, columns) - return createValueSelection(nextPath, false) + return createValueSelection(nextPath) } return selection @@ -297,7 +297,7 @@ export function selectPreviousColumn(columns: JSONPath[], selection: JSONSelecti if (columnIndex > 0) { const previousPosition = { rowIndex, columnIndex: columnIndex - 1 } const previousPath = fromTableCellPosition(previousPosition, columns) - return createValueSelection(previousPath, false) + return createValueSelection(previousPath) } return selection @@ -309,7 +309,7 @@ export function selectNextColumn(columns: JSONPath[], selection: JSONSelection): if (columnIndex < columns.length - 1) { const nextPosition = { rowIndex, columnIndex: columnIndex + 1 } const nextPath = fromTableCellPosition(nextPosition, columns) - return createValueSelection(nextPath, false) + return createValueSelection(nextPath) } return selection diff --git a/src/lib/plugins/value/components/EditableValue.svelte b/src/lib/plugins/value/components/EditableValue.svelte index 4ff13c14..4375b5b0 100644 --- a/src/lib/plugins/value/components/EditableValue.svelte +++ b/src/lib/plugins/value/components/EditableValue.svelte @@ -4,12 +4,13 @@ import type { JSONPatchDocument, JSONPath } from 'immutable-json-patch' import { compileJSONPointer } from 'immutable-json-patch' import { isObjectOrArray, stringConvert } from '$lib/utils/typeUtils.js' - import { createValueSelection, getFocusPath } from '$lib/logic/selection.js' + import { createValueSelection, getFocusPath, isEditingSelection } from '$lib/logic/selection.js' import { getValueClass } from '$lib/plugins/value/components/utils/getValueClass.js' import EditableDiv from '../../../components/controls/EditableDiv.svelte' import type { FindNextInside, JSONParser, + JSONSelection, OnFind, OnJSONSelect, OnPasteJson, @@ -22,6 +23,7 @@ export let path: JSONPath export let value: unknown + export let selection: JSONSelection | undefined export let parser: JSONParser export let normalization: ValueNormalization export let enforceString: boolean @@ -56,7 +58,7 @@ const selection = updateSelection === UpdateSelectionAfterChange.nextInside ? findNextInside(path) - : createValueSelection(path, false) + : createValueSelection(path) return { state: patchedState, @@ -69,7 +71,7 @@ } function handleCancelChange() { - onSelect(createValueSelection(path, false)) + onSelect(createValueSelection(path)) focus() } @@ -112,6 +114,7 @@ import { isUrl } from '$lib/utils/typeUtils.js' - import { createValueSelection } from '$lib/logic/selection.js' + import { createEditValueSelection } from '$lib/logic/selection.js' import SearchResultHighlighter from '../../../components/modes/treemode/highlight/SearchResultHighlighter.svelte' import { getValueClass } from './utils/getValueClass.js' import { addNewLineSuffix } from '$lib/utils/domUtils.js' @@ -38,7 +38,7 @@ function handleValueDoubleClick(event: MouseEvent) { if (!readOnly) { event.preventDefault() - onSelect(createValueSelection(path, true)) + onSelect(createEditValueSelection(path)) } } diff --git a/src/lib/plugins/value/renderValue.ts b/src/lib/plugins/value/renderValue.ts index 7ee124c1..75441f3c 100644 --- a/src/lib/plugins/value/renderValue.ts +++ b/src/lib/plugins/value/renderValue.ts @@ -1,15 +1,16 @@ -import { isBoolean, isColor, isTimestamp } from '../../utils/typeUtils.js' +import { isBoolean, isColor, isTimestamp } from '$lib/utils/typeUtils.js' +import type { RenderValueComponentDescription, RenderValueProps } from '$lib/types' import BooleanToggle from './components/BooleanToggle.svelte' import ColorPicker from './components/ColorPicker.svelte' import EditableValue from './components/EditableValue.svelte' import ReadonlyValue from './components/ReadonlyValue.svelte' import TimestampTag from './components/TimestampTag.svelte' -import type { RenderValueComponentDescription, RenderValueProps } from '../../types' export function renderValue({ path, value, readOnly, + selection, enforceString, searchResultItems, isEditing, @@ -44,6 +45,7 @@ export function renderValue({ props: { path, value, + selection, enforceString, parser, normalization, diff --git a/src/lib/types.ts b/src/lib/types.ts index 876d4723..9ddc9be0 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -201,13 +201,25 @@ export interface InsideSelection { export interface KeySelection { type: SelectionType.key path: JSONPath - edit?: boolean } -export interface ValueSelection { +export interface EditKeySelection extends KeySelection { + type: SelectionType.key + path: JSONPath + edit: true + initialValue?: string +} + +export type ValueSelection = { + type: SelectionType.value + path: JSONPath +} + +export interface EditValueSelection extends ValueSelection { type: SelectionType.value path: JSONPath - edit?: boolean + edit: true + initialValue?: string } export type JSONSelection = @@ -215,7 +227,9 @@ export type JSONSelection = | AfterSelection | InsideSelection | KeySelection + | EditKeySelection | ValueSelection + | EditValueSelection // TextSelection is the result of EditorSelection.toJSON() from CodeMirror, // with an additional `type` property diff --git a/src/lib/utils/domUtils.ts b/src/lib/utils/domUtils.ts index feeabdbf..5dd1d837 100644 --- a/src/lib/utils/domUtils.ts +++ b/src/lib/utils/domUtils.ts @@ -223,33 +223,6 @@ export function setCursorToEnd(element: HTMLElement) { selection?.addRange(range) } -/** - * Insert (append or replace) the text contents of the current active element - */ -export function insertActiveElementContents( - container: HTMLElement, - text: string, - replaceContents: boolean, - onActiveElement?: (activeElement: HTMLElement) => void -) { - const window = getWindow(container) - if (!window) { - return - } - - const activeElement: HTMLElement | undefined = window.document.activeElement - ? (window.document.activeElement as HTMLElement) - : undefined - - if (activeElement && activeElement.isContentEditable) { - activeElement.textContent = replaceContents ? text : activeElement.textContent + text - setCursorToEnd(activeElement) - if (onActiveElement) { - onActiveElement(activeElement) - } - } -} - /** * Gets a DOM element's Window. This is normally just the global `window` * variable, but if we opened a child window, it may be different. diff --git a/src/routes/components/EditableValueInput.svelte b/src/routes/components/EditableValueInput.svelte index c0aa8fd6..e0b7d495 100644 --- a/src/routes/components/EditableValueInput.svelte +++ b/src/routes/components/EditableValueInput.svelte @@ -64,7 +64,7 @@ } function cancel() { - onSelect(createValueSelection(path, false)) + onSelect(createValueSelection(path)) focus() } diff --git a/src/routes/components/EvaluatorAction.ts b/src/routes/components/EvaluatorAction.ts index 2fcc9d44..5fb226e2 100644 --- a/src/routes/components/EvaluatorAction.ts +++ b/src/routes/components/EvaluatorAction.ts @@ -1,6 +1,7 @@ -import { createValueSelection, type OnJSONSelect } from 'svelte-jsoneditor' +import { type OnJSONSelect } from 'svelte-jsoneditor' import type { Action } from 'svelte/action' import { type JSONPath } from 'immutable-json-patch' +import { createEditValueSelection } from '$lib/logic/selection' export interface EvaluatorActionProps { value: unknown @@ -25,7 +26,7 @@ export const EvaluatorAction: Action> = event.stopPropagation() // open in edit mode - props.onSelect(createValueSelection(props.path, true)) + props.onSelect(createEditValueSelection(props.path)) } } diff --git a/src/routes/development/+page.svelte b/src/routes/development/+page.svelte index bd0161bf..11df2a21 100644 --- a/src/routes/development/+page.svelte +++ b/src/routes/development/+page.svelte @@ -663,7 +663,7 @@