Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API for expand and collapse #458

Merged
merged 21 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d47c6b6
chore: refactor `expandWithCallback` to use `updateInDocumentState`
josdejong Jun 17, 2024
11ba476
chore: remove `expandSingleItem`
josdejong Jun 17, 2024
d26b516
chore: write unit tests for `pathStartsWith`
josdejong Jun 17, 2024
f05474b
chore: remove redundant usage of `expandWithCallback` from `TableMode`
josdejong Jun 26, 2024
8c4272c
chore: refactor `expandWithCallback` and `expandPath` (WIP)
josdejong Jun 26, 2024
a8b82ca
Merge branch 'refs/heads/v1' into feat/expand-and-collapse
josdejong Jun 26, 2024
5ba8657
fix: expand visible section in `expandParentPath`
josdejong Jun 27, 2024
d0fccc9
chore: merge `expandParentPath` and `expandWithCallback` into `expand…
josdejong Jun 27, 2024
0767008
chore: rename `expandSmart` and some refactoring
josdejong Jun 27, 2024
80d5fa9
chore: merge `getSmartExpand` into `expandSmart`
josdejong Jun 27, 2024
356b68c
chore: refactor `expandPath` (WIP)
josdejong Jul 1, 2024
14e8dd8
chore: refactor `expandPath` (WIP)
josdejong Jul 1, 2024
124b190
chore: write unit tests for `expandSmart` and more
josdejong Jul 1, 2024
fa7704d
chore: change `expandPath` to default to a callback `expandSelf`, exp…
josdejong Jul 1, 2024
090ee42
feat: implement method `collapse(path, recursive)`
josdejong Jul 1, 2024
863d5f6
fix: reset visibleSections when collapsing an array
josdejong Jul 1, 2024
7081335
chore: add a couple of unit tests
josdejong Jul 4, 2024
39d774c
chore: create `_transformDocumentState` and use it in `syncDocumentSt…
josdejong Jul 4, 2024
638fcaf
chore: in `_transformDocumentState`, only iterate over visible sectio…
josdejong Jul 5, 2024
9f1597f
chore: refactor `_expandRecursively` to use `_transformDocumentState`
josdejong Jul 5, 2024
af42a13
docs: clarify the docs on method `expand`
josdejong Jul 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ Invoked when the mode is changed.
#### onClassName

```ts
onClassName(path: Path, value: any): string | undefined
onClassName(path: JSONPath, value: any): string | undefined
```

Add a custom class name to specific nodes, based on their path and/or value. Note that in the custom class, you can override CSS variables like `--jse-contents-background-color` to change the styling of a node, like the background color. Relevant variables are:
Expand Down Expand Up @@ -712,14 +712,33 @@ editor.updateProps({
#### expand

```ts
JSONEditor.prototype.expand([callback: (path: Path) => boolean]): Promise<void>
JSONEditor.prototype.expand(path: JSONPath, callback?: (relativePath: JSONPath) => boolean = expandSelf): Promise<void>
```

Expand or collapse paths in the editor. The `callback` determines which paths will be expanded. If no `callback` is provided, all paths will be expanded. It is only possible to expand a path when all of its parent paths are expanded too. Examples:
Expand or collapse paths in the editor. All nodes along the provided `path` will be expanded and become visible (rendered). So for example collapsed sections of an array will be expanded. Using the optional `callback`, the node itself and some or all of its nested child nodes can be expanded too. The `callback` function only iterates over the visible sections of an array and not over any of the collapsed sections. By default, the first 100 items of an array are visible and rendered.

- `editor.expand(path => true)` expand all
- `editor.expand(path => false)` collapse all
- `editor.expand(path => path.length < 2)` expand all paths up to 2 levels deep
Examples:

- `editor.expand([], () => true)` expand all
- `editor.expand([], relativePath => relativePath.length < 2)` expand all paths up to 2 levels deep
- `editor.expand(['array', '204'])` expand the root object, the array in this object, and the 204th item in the array.
- `editor.expand(['array', '204'], () => false)` expand the root object, the array in this object, but not the 204th item itself.
- `editor.expand(['array', '204'], relativePath => relativePath.length < 2)` expand the root object, the array in this object, and expand the 204th array item and all of its child's up to a depth of max 2 levels.

The library exports a couple of utility functions for commonly used `callback` functions:

- `expandAll`: recursively expand all nested objects and arrays.
- `expandNone`: expand nothing, also not the root object or array.
- `expandSelf`: expand the root array or object. This is the default for the `callback` parameter.
- `expandMinimal`: expand the root array or object, and in case of an array, expand the first array item.

### collapse

```ts
JSONEditor.prototype.collapse(path: JSONPath, recursive?: boolean = false): Promise<void>
```

Collapse a path in the editor. When `recursive` is `true`, all nested objects and arrays will be collapsed too. The default value of `recursive` is `false`.

#### transform

Expand All @@ -732,15 +751,15 @@ Programmatically trigger clicking of the transform button in the main menu, open
#### scrollTo

```ts
JSONEditor.prototype.scrollTo(path: Path): Promise<void>
JSONEditor.prototype.scrollTo(path: JSONPath): Promise<void>
```

Scroll the editor vertically such that the specified path comes into view. Only applicable to modes `tree` and `table`. The path will be expanded when needed. The returned Promise is resolved after scrolling is finished.

#### findElement

```ts
JSONEditor.prototype.findElement(path: Path)
JSONEditor.prototype.findElement(path: JSONPath)
```

Find the DOM element of a given path. Returns `null` when not found.
Expand Down Expand Up @@ -821,6 +840,11 @@ The library exports a set of utility functions. The exact definitions of those f
- `toTextContent`
- `toJSONContent`
- `estimateSerializedSize`
- Expand:
- `expandAll`
- `expandMinimal`
- `expandNone`
- `expandSelf`
- Selection:
- `isValueSelection`
- `isKeySelection`
Expand Down
10 changes: 8 additions & 2 deletions src/lib/components/JSONEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,14 @@
await tick() // await rerender
}

export async function expand(callback?: OnExpand): Promise<void> {
refJSONEditorRoot.expand(callback)
export async function expand(path: JSONPath, callback?: OnExpand): Promise<void> {
refJSONEditorRoot.expand(path, callback)

await tick() // await rerender
}

export async function collapse(path: JSONPath, recursive = false): Promise<void> {
refJSONEditorRoot.collapse(path, recursive)

await tick() // await rerender
}
Expand Down
12 changes: 10 additions & 2 deletions src/lib/components/modes/JSONEditorRoot.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,22 @@
throw new Error(`Method patch is not available in mode "${mode}"`)
}

export function expand(callback?: OnExpand): void {
export function expand(path: JSONPath, callback?: OnExpand): void {
if (refTreeMode) {
return refTreeMode.expand(callback)
return refTreeMode.expand(path, callback)
} else {
throw new Error(`Method expand is not available in mode "${mode}"`)
}
}

export function collapse(path: JSONPath, recursive: boolean): void {
if (refTreeMode) {
return refTreeMode.collapse(path, recursive)
} else {
throw new Error(`Method collapse is not available in mode "${mode}"`)
}
}

/**
* Open the transform modal
*/
Expand Down
23 changes: 3 additions & 20 deletions src/lib/components/modes/tablemode/TableMode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,6 @@
import {
createDocumentState,
documentStatePatch,
expandMinimal,
expandWithCallback,
getEnforceString,
getInRecursiveState,
setInDocumentState,
Expand Down Expand Up @@ -1369,12 +1367,7 @@
const previousContent = { json, text }
const previousState = { json, documentState, selection, sortedColumn, text, textIsRepaired }

const updatedState = expandWithCallback(
json,
syncDocumentState(updatedJson, documentState),
[],
expandMinimal
)
const updatedState = syncDocumentState(updatedJson, documentState)

const callback =
typeof afterPatch === 'function'
Expand Down Expand Up @@ -1411,24 +1404,14 @@

try {
json = parseMemoizeOne(updatedText)
documentState = expandWithCallback(
json,
syncDocumentState(json, documentState),
[],
expandMinimal
)
documentState = syncDocumentState(json, documentState)
text = undefined
textIsRepaired = false
parseError = undefined
} catch (err) {
try {
json = parseMemoizeOne(jsonrepair(updatedText))
documentState = expandWithCallback(
json,
syncDocumentState(json, documentState),
[],
expandMinimal
)
documentState = syncDocumentState(json, documentState)
text = updatedText
textIsRepaired = true
parseError = undefined
Expand Down
78 changes: 33 additions & 45 deletions src/lib/components/modes/treemode/TreeMode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@
documentStatePatch,
expandAll,
expandMinimal,
expandNone,
expandPath,
expandRecursive,
expandSection,
expandSingleItem,
expandWithCallback,
getDefaultExpand,
expandSelf,
expandSmart,
getEnforceString,
setInDocumentState,
syncDocumentState
Expand Down Expand Up @@ -136,13 +135,13 @@
OnTransformModal,
ParseError,
PastedJson,
SearchResults,
ValidationErrors,
SearchResultDetails,
SearchResults,
Section,
TransformModalOptions,
TreeModeContext,
ValidationError,
ValidationErrors,
Validator,
ValueNormalization
} from '$lib/types'
Expand Down Expand Up @@ -301,7 +300,7 @@
}

async function handleFocusSearch(path: JSONPath) {
documentState = expandPath(json, documentState, path)
documentState = expandPath(json, documentState, path, expandNone)
await scrollTo(path)
}

Expand All @@ -325,11 +324,22 @@
})
let historyState = history.getState()

export function expand(callback: OnExpand = expandAll) {
export function expand(path: JSONPath, callback: OnExpand = expandSelf) {
debug('expand')

// FIXME: clear the expanded state and visible sections (else you can't collapse anything using the callback)
documentState = expandWithCallback(json, documentState, [], callback)
documentState = expandPath(json, documentState, path, callback)
}

export function collapse(path: JSONPath, recursive: boolean) {
documentState = collapsePath(json, documentState, path, recursive)

if (selection) {
// check whether the selection is still visible and not collapsed
if (isSelectionInsidePath(selection, path)) {
// remove selection when not visible anymore
updateSelection(undefined)
}
}
}

// two-way binding of externalContent and internal json and text (
Expand Down Expand Up @@ -511,7 +521,7 @@
function expandWhenNotInitialized(json: unknown) {
if (!documentStateInitialized) {
documentStateInitialized = true
documentState = createDocumentState({ json, expand: getDefaultExpand(json) })
documentState = expandSmart(json, documentState, [])
}
}

Expand Down Expand Up @@ -843,7 +853,7 @@
// expand extracted object/array
const path: JSONPath = []
return {
state: expandRecursive(patchedJson, patchedState, path)
state: expandSmart(patchedJson, patchedState, path)
}
}

Expand Down Expand Up @@ -911,7 +921,7 @@
// expand converted object/array
return {
state: selection
? expandRecursive(patchedJson, patchedState, getFocusPath(selection))
? expandSmart(patchedJson, patchedState, getFocusPath(selection))
: documentState
}
})
Expand Down Expand Up @@ -1074,7 +1084,7 @@

handlePatch(operations, (patchedJson, patchedState) => ({
// expand the newly replaced array and select it
state: expandRecursive(patchedJson, patchedState, rootPath),
state: expandSmart(patchedJson, patchedState, rootPath),
selection: createValueSelection(rootPath, false)
}))
},
Expand Down Expand Up @@ -1128,7 +1138,7 @@

handlePatch(operations, (patchedJson, patchedState) => ({
// expand the newly replaced array and select it
state: expandRecursive(patchedJson, patchedState, rootPath),
state: expandSmart(patchedJson, patchedState, rootPath),
selection: createValueSelection(rootPath, false)
}))
}
Expand Down Expand Up @@ -1184,7 +1194,7 @@
* Expand the path when needed.
*/
export async function scrollTo(path: JSONPath, scrollToWhenVisible = true): Promise<void> {
documentState = expandPath(json, documentState, path)
documentState = expandPath(json, documentState, path, expandNone)
await tick() // await rerender (else the element we want to scroll to does not yet exist)

const elem = findElement(path)
Expand Down Expand Up @@ -1302,7 +1312,7 @@
const previousContent = { json, text }
const previousState = { documentState, selection, json, text, textIsRepaired }

const updatedState = expandWithCallback(
const updatedState = expandPath(
json,
syncDocumentState(updatedJson, documentState),
[],
Expand Down Expand Up @@ -1342,24 +1352,14 @@

try {
json = parseMemoizeOne(updatedText)
documentState = expandWithCallback(
json,
syncDocumentState(json, documentState),
[],
expandMinimal
)
documentState = expandPath(json, syncDocumentState(json, documentState), [], expandMinimal)
text = undefined
textIsRepaired = false
parseError = undefined
} catch (err) {
try {
json = parseMemoizeOne(jsonrepair(updatedText))
documentState = expandWithCallback(
json,
syncDocumentState(json, documentState),
[],
expandMinimal
)
documentState = expandPath(json, syncDocumentState(json, documentState), [], expandMinimal)
text = updatedText
textIsRepaired = true
parseError = undefined
Expand Down Expand Up @@ -1398,28 +1398,16 @@
/**
* Toggle expanded state of a node
* @param path The path to be expanded
* @param expanded True to expand, false to collapse
* @param expanded True if currently expanded, false when currently collapsed
* @param [recursive=false] Only applicable when expanding
*/
function handleExpand(path: JSONPath, expanded: boolean, recursive = false): void {
debug('handleExpand', { path, expanded, recursive })

if (expanded) {
if (recursive) {
documentState = expandWithCallback(json, documentState, path, expandAll)
} else {
documentState = expandSingleItem(json, documentState, path)
}
expand(path, recursive ? expandAll : expandSelf)
} else {
documentState = collapsePath(json, documentState, path)

if (selection) {
// check whether the selection is still visible and not collapsed
if (isSelectionInsidePath(selection, path)) {
// remove selection when not visible anymore
updateSelection(undefined)
}
}
collapse(path, recursive)
}

// set focus to the hidden input, so we can capture quick keys like Ctrl+X, Ctrl+C, Ctrl+V
Expand Down Expand Up @@ -1802,7 +1790,7 @@

handlePatch(operations, (patchedJson, patchedState) => {
return {
state: expandRecursive(patchedJson, patchedState, path)
state: expandSmart(patchedJson, patchedState, path)
}
})

Expand Down
3 changes: 3 additions & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export {
estimateSerializedSize
} from './utils/jsonUtils.js'

// expand
export { expandAll, expandMinimal, expandNone, expandSelf } from './logic/documentState'

// selection
export {
isValueSelection,
Expand Down
Loading