Create global state containing expanded state
This commit is contained in:
parent
d2a84b29fe
commit
d04c94a1c6
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { EXPANDED_PROPERTY } from './constants.js'
|
||||||
import SearchBox from './SearchBox.svelte'
|
import SearchBox from './SearchBox.svelte'
|
||||||
import Icon from 'svelte-awesome'
|
import Icon from 'svelte-awesome'
|
||||||
import { faSearch, faUndo, faRedo } from '@fortawesome/free-solid-svg-icons'
|
import { faSearch, faUndo, faRedo } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
@ -14,6 +15,10 @@
|
||||||
export let onChangeJson = () => {
|
export let onChangeJson = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let state = {
|
||||||
|
[EXPANDED_PROPERTY]: true
|
||||||
|
}
|
||||||
|
|
||||||
let showSearch = true // FIXME: change to false
|
let showSearch = true // FIXME: change to false
|
||||||
let searchText = ''
|
let searchText = ''
|
||||||
|
|
||||||
|
@ -33,10 +38,19 @@
|
||||||
history.clear()
|
history.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyPatch (operations) {
|
||||||
|
const patchResult = immutableJSONPatch(json, operations)
|
||||||
|
json = patchResult.json
|
||||||
|
|
||||||
|
state = immutableJSONPatch(state, operations).json
|
||||||
|
|
||||||
|
return patchResult
|
||||||
|
}
|
||||||
|
|
||||||
export function patch(operations) {
|
export function patch(operations) {
|
||||||
console.log('patch', operations)
|
console.log('patch', operations)
|
||||||
|
|
||||||
const patchResult = immutableJSONPatch(json, operations)
|
const patchResult = applyPatch(operations)
|
||||||
|
|
||||||
history.add({
|
history.add({
|
||||||
undo: patchResult.revert,
|
undo: patchResult.revert,
|
||||||
|
@ -123,7 +137,7 @@
|
||||||
if (history.getState().canUndo) {
|
if (history.getState().canUndo) {
|
||||||
const item = history.undo()
|
const item = history.undo()
|
||||||
if (item) {
|
if (item) {
|
||||||
json = immutableJSONPatch(json, item.undo).json
|
applyPatch(item.undo)
|
||||||
emitOnChange()
|
emitOnChange()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,12 +147,21 @@
|
||||||
if (history.getState().canRedo) {
|
if (history.getState().canRedo) {
|
||||||
const item = history.redo()
|
const item = history.redo()
|
||||||
if (item) {
|
if (item) {
|
||||||
json = immutableJSONPatch(json, item.redo).json
|
applyPatch(item.redo)
|
||||||
emitOnChange()
|
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) {
|
function handleKeyDown (event) {
|
||||||
const combo = keyComboFromEvent(event)
|
const combo = keyComboFromEvent(event)
|
||||||
|
|
||||||
|
@ -228,10 +251,11 @@
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<Node
|
<Node
|
||||||
value={json}
|
value={json}
|
||||||
|
state={state}
|
||||||
searchResult={searchResultWithActive}
|
searchResult={searchResultWithActive}
|
||||||
expanded={true}
|
|
||||||
onChangeKey={handleChangeKey}
|
onChangeKey={handleChangeKey}
|
||||||
onPatch={handlePatch}
|
onPatch={handlePatch}
|
||||||
|
onExpand={handleExpand}
|
||||||
getParentPath={getPath}
|
getParentPath={getPath}
|
||||||
/>
|
/>
|
||||||
<div class='bottom'></div>
|
<div class='bottom'></div>
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
<script>
|
<script>
|
||||||
|
import {
|
||||||
|
EXPANDED_PROPERTY,
|
||||||
|
SEARCH_PROPERTY,
|
||||||
|
SEARCH_VALUE
|
||||||
|
} from './constants.js'
|
||||||
import { getPlainText, setPlainText } from './utils/domUtils.js'
|
import { getPlainText, setPlainText } from './utils/domUtils.js'
|
||||||
import Icon from 'svelte-awesome'
|
import Icon from 'svelte-awesome'
|
||||||
import { faCaretDown, faCaretRight } from '@fortawesome/free-solid-svg-icons'
|
import { faCaretDown, faCaretRight } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { SEARCH_PROPERTY, SEARCH_VALUE } from './utils/search.js'
|
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import debounce from 'lodash/debounce'
|
import debounce from 'lodash/debounce'
|
||||||
import { findUniqueName } from './utils/stringUtils.js'
|
import { findUniqueName } from './utils/stringUtils.js'
|
||||||
|
@ -12,13 +16,16 @@
|
||||||
|
|
||||||
export let key = undefined // only applicable for object properties
|
export let key = undefined // only applicable for object properties
|
||||||
export let value
|
export let value
|
||||||
|
export let state
|
||||||
export let searchResult
|
export let searchResult
|
||||||
export let onPatch
|
export let onPatch
|
||||||
export let onChangeKey
|
export let onChangeKey
|
||||||
export let expanded = false
|
export let onExpand
|
||||||
|
|
||||||
export let getParentPath
|
export let getParentPath
|
||||||
|
|
||||||
|
$: expanded = state && state[EXPANDED_PROPERTY]
|
||||||
|
|
||||||
function getPath () {
|
function getPath () {
|
||||||
return key !== undefined
|
return key !== undefined
|
||||||
? getParentPath().concat(key)
|
? getParentPath().concat(key)
|
||||||
|
@ -93,7 +100,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle () {
|
function toggle () {
|
||||||
expanded = !expanded
|
onExpand(getPath(), !expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateKey () {
|
function updateKey () {
|
||||||
|
@ -238,7 +245,7 @@
|
||||||
<div class="delimiter">[</div>
|
<div class="delimiter">[</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="delimiter">[</div>
|
<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>
|
<div class="delimiter">]</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -248,9 +255,11 @@
|
||||||
<svelte:self
|
<svelte:self
|
||||||
key={index}
|
key={index}
|
||||||
value={item}
|
value={item}
|
||||||
|
state={state && state[index]}
|
||||||
searchResult={searchResult ? searchResult[index] : undefined}
|
searchResult={searchResult ? searchResult[index] : undefined}
|
||||||
onChangeKey={handleChangeKey}
|
onChangeKey={handleChangeKey}
|
||||||
onPatch={onPatch}
|
onPatch={onPatch}
|
||||||
|
onExpand={onExpand}
|
||||||
getParentPath={getPath}
|
getParentPath={getPath}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -288,7 +297,7 @@
|
||||||
<span class="delimiter">{</span>
|
<span class="delimiter">{</span>
|
||||||
{:else}
|
{:else}
|
||||||
<span class="delimiter"> {</span>
|
<span class="delimiter"> {</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>
|
<span class="delimiter">}</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -298,9 +307,11 @@
|
||||||
<svelte:self
|
<svelte:self
|
||||||
key={prop.key}
|
key={prop.key}
|
||||||
value={value[prop.key]}
|
value={value[prop.key]}
|
||||||
|
state={state && state[prop.key]}
|
||||||
searchResult={searchResult ? searchResult[prop.key] : undefined}
|
searchResult={searchResult ? searchResult[prop.key] : undefined}
|
||||||
onChangeKey={handleChangeKey}
|
onChangeKey={handleChangeKey}
|
||||||
onPatch={onPatch}
|
onPatch={onPatch}
|
||||||
|
onExpand={onExpand}
|
||||||
getParentPath={getPath}
|
getParentPath={getPath}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
export const EXPANDED_PROPERTY = '$jse:expanded'
|
||||||
|
export const SEARCH_PROPERTY = '$jse:search:property'
|
||||||
|
export const SEARCH_VALUE = '$jse:search:value'
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {string[]} Path
|
* @typedef {<string | number>[]} Path
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -70,12 +70,22 @@ export function setIn (object, path, value) {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isObjectOrArray(object)) {
|
// TODO: rewrite this function into a while loop instead of recursive (like getIn)
|
||||||
throw new Error('Path does not exist')
|
|
||||||
}
|
|
||||||
|
|
||||||
const key = path[0]
|
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) {
|
if (object[key] === updatedValue) {
|
||||||
// return original object unchanged when the new value is identical to the old one
|
// return original object unchanged when the new value is identical to the old one
|
||||||
return object
|
return object
|
||||||
|
|
|
@ -59,19 +59,32 @@ test('setIn basic', () => {
|
||||||
expect(obj).not.toBe(updated)
|
expect(obj).not.toBe(updated)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('setIn non existing path', () => {
|
test('setIn non existing path should create the path (1)', () => {
|
||||||
const obj = {}
|
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 = {
|
const obj = {
|
||||||
a: 42,
|
a: 42,
|
||||||
d: 3
|
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', () => {
|
test('setIn replace value inside nested array', () => {
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
|
import { SEARCH_PROPERTY, SEARCH_VALUE } from '../constants.js'
|
||||||
import { valueType } from './typeUtils.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) {
|
export function search (key, value, searchText) {
|
||||||
let results = undefined
|
let results = undefined
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue