Create global state containing expanded state

This commit is contained in:
josdejong 2020-05-23 20:45:28 +02:00
parent d2a84b29fe
commit d04c94a1c6
7 changed files with 82 additions and 21 deletions

View File

@ -1,4 +1,5 @@
<script>
import { EXPANDED_PROPERTY } from './constants.js'
import SearchBox from './SearchBox.svelte'
import Icon from 'svelte-awesome'
import { faSearch, faUndo, faRedo } from '@fortawesome/free-solid-svg-icons'
@ -14,6 +15,10 @@
export let onChangeJson = () => {
}
let state = {
[EXPANDED_PROPERTY]: true
}
let showSearch = true // FIXME: change to false
let searchText = ''
@ -33,10 +38,19 @@
history.clear()
}
function applyPatch (operations) {
const patchResult = immutableJSONPatch(json, operations)
json = patchResult.json
state = immutableJSONPatch(state, operations).json
return patchResult
}
export function patch(operations) {
console.log('patch', operations)
const patchResult = immutableJSONPatch(json, operations)
const patchResult = applyPatch(operations)
history.add({
undo: patchResult.revert,
@ -123,7 +137,7 @@
if (history.getState().canUndo) {
const item = history.undo()
if (item) {
json = immutableJSONPatch(json, item.undo).json
applyPatch(item.undo)
emitOnChange()
}
}
@ -133,12 +147,21 @@
if (history.getState().canRedo) {
const item = history.redo()
if (item) {
json = immutableJSONPatch(json, item.redo).json
applyPatch(item.redo)
emitOnChange()
}
}
}
/**
* Toggle expanded state of a node
* @param {Path} path
* @param {boolean} expanded
*/
function handleExpand (path, expanded) {
state = setIn(state, path.concat(EXPANDED_PROPERTY), expanded)
}
function handleKeyDown (event) {
const combo = keyComboFromEvent(event)
@ -228,10 +251,11 @@
<div class="contents">
<Node
value={json}
state={state}
searchResult={searchResultWithActive}
expanded={true}
onChangeKey={handleChangeKey}
onPatch={handlePatch}
onExpand={handleExpand}
getParentPath={getPath}
/>
<div class='bottom'></div>

View File

@ -1,8 +1,12 @@
<script>
import {
EXPANDED_PROPERTY,
SEARCH_PROPERTY,
SEARCH_VALUE
} from './constants.js'
import { getPlainText, setPlainText } from './utils/domUtils.js'
import Icon from 'svelte-awesome'
import { faCaretDown, faCaretRight } from '@fortawesome/free-solid-svg-icons'
import { SEARCH_PROPERTY, SEARCH_VALUE } from './utils/search.js'
import classnames from 'classnames'
import debounce from 'lodash/debounce'
import { findUniqueName } from './utils/stringUtils.js'
@ -12,13 +16,16 @@
export let key = undefined // only applicable for object properties
export let value
export let state
export let searchResult
export let onPatch
export let onChangeKey
export let expanded = false
export let onExpand
export let getParentPath
$: expanded = state && state[EXPANDED_PROPERTY]
function getPath () {
return key !== undefined
? getParentPath().concat(key)
@ -93,7 +100,7 @@
}
function toggle () {
expanded = !expanded
onExpand(getPath(), !expanded)
}
function updateKey () {
@ -238,7 +245,7 @@
<div class="delimiter">[</div>
{:else}
<div class="delimiter">[</div>
<button class="tag" on:click={() => expanded = true}>{value.length} items</button>
<button class="tag" on:click={() => onExpand(getPath(), true)}>{value.length} items</button>
<div class="delimiter">]</div>
{/if}
</div>
@ -248,9 +255,11 @@
<svelte:self
key={index}
value={item}
state={state && state[index]}
searchResult={searchResult ? searchResult[index] : undefined}
onChangeKey={handleChangeKey}
onPatch={onPatch}
onExpand={onExpand}
getParentPath={getPath}
/>
{/each}
@ -288,7 +297,7 @@
<span class="delimiter">&#123;</span>
{:else}
<span class="delimiter"> &#123;</span>
<button class="tag" on:click={() => expanded = true}>{props.length} props</button>
<button class="tag" on:click={() => onExpand(getPath(), true)}>{props.length} props</button>
<span class="delimiter">}</span>
{/if}
</div>
@ -298,9 +307,11 @@
<svelte:self
key={prop.key}
value={value[prop.key]}
state={state && state[prop.key]}
searchResult={searchResult ? searchResult[prop.key] : undefined}
onChangeKey={handleChangeKey}
onPatch={onPatch}
onExpand={onExpand}
getParentPath={getPath}
/>
{/each}

5
src/constants.js Normal file
View File

@ -0,0 +1,5 @@
export const EXPANDED_PROPERTY = '$jse:expanded'
export const SEARCH_PROPERTY = '$jse:search:property'
export const SEARCH_VALUE = '$jse:search:value'

View File

@ -25,7 +25,7 @@
*/
/**
* @typedef {string[]} Path
* @typedef {<string | number>[]} Path
*/
/**

View File

@ -70,12 +70,22 @@ export function setIn (object, path, value) {
return value
}
if (!isObjectOrArray(object)) {
throw new Error('Path does not exist')
}
// TODO: rewrite this function into a while loop instead of recursive (like getIn)
const key = path[0]
const updatedValue = setIn(object[key], path.slice(1), value)
// TODO: check whether path is string -> object expected, path is number -> array expected
const updatedValue = setIn(object && object[key], path.slice(1), value)
if (!isObjectOrArray(object)) {
const newObject = typeof key === 'number'
? []
: {}
newObject[key] = updatedValue
return newObject
}
if (object[key] === updatedValue) {
// return original object unchanged when the new value is identical to the old one
return object

View File

@ -59,19 +59,32 @@ test('setIn basic', () => {
expect(obj).not.toBe(updated)
})
test('setIn non existing path', () => {
test('setIn non existing path should create the path (1)', () => {
const obj = {}
expect(() => setIn(obj, ['a', 'b', 'c'], 4)).toThrow(/Path does not exist/)
expect(setIn(obj, ['a', 'b', 'c'], 4)).toEqual({
a: {
b: {
c: 4
}
}
})
})
test('setIn replace value with object should throw an exception', () => {
test('setIn on non-existing path should create the path (2)', () => {
const obj = {
a: 42,
d: 3
}
expect(() => setIn(obj, ['a', 'b', 'c'], 4)).toThrow(/Path does not exist/)
expect(setIn(obj, ['a', 'b', 'c'], 4)).toEqual({
a: {
b: {
c: 4
}
},
d: 3
})
})
test('setIn replace value inside nested array', () => {

View File

@ -1,8 +1,6 @@
import { SEARCH_PROPERTY, SEARCH_VALUE } from '../constants.js'
import { valueType } from './typeUtils.js'
export const SEARCH_PROPERTY = '$jse:search:property'
export const SEARCH_VALUE = '$jse:search:value'
export function search (key, value, searchText) {
let results = undefined