Create global state containing expanded state
This commit is contained in:
parent
d2a84b29fe
commit
d04c94a1c6
|
@ -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>
|
||||
|
|
|
@ -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">{</span>
|
||||
{:else}
|
||||
<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>
|
||||
{/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}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue