Skip to content

Commit

Permalink
feat: change most methods like set, update, expand, and `collap…
Browse files Browse the repository at this point in the history
…se` to synchrounous, fixing #499 (#524)
  • Loading branch information
josdejong authored Feb 21, 2025
1 parent 2eb88f2 commit 6dd69cd
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 69 deletions.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -688,15 +688,15 @@ Get the current JSON document.
#### set
```ts
JSONEditor.prototype.set(content: Content): Promise<void>
JSONEditor.prototype.set(content: Content): void
```
Replace the current content. Will reset the state of the editor. See also method `update(content)`.
#### update
```ts
JSONEditor.prototype.update(content: Content): Promise<void>
JSONEditor.prototype.update(content: Content): void
```
Update the loaded content, keeping the state of the editor (like expanded objects). You can also call `editor.updateProps({ content })`. See also method `set(content)`.
Expand All @@ -706,7 +706,7 @@ Update the loaded content, keeping the state of the editor (like expanded object
#### patch
```ts
JSONEditor.prototype.patch(operations: JSONPatchDocument) : Promise<JSONPatchResult>
JSONEditor.prototype.patch(operations: JSONPatchDocument) : JSONPatchResult
```
Apply a JSON patch document to update the contents of the JSON document. A JSON patch document is a list with JSON Patch operations.
Expand All @@ -716,7 +716,7 @@ Apply a JSON patch document to update the contents of the JSON document. A JSON
#### updateProps
```ts
JSONEditor.prototype.updateProps(props: Object): Promise<void>
JSONEditor.prototype.updateProps(props: Object): void
```
Tpdate some or all of the properties. Updated `content` can be passed too; this is equivalent to calling `update(content)`. Example:
Expand All @@ -730,7 +730,7 @@ editor.updateProps({
#### expand
```ts
JSONEditor.prototype.expand(path: JSONPath, callback?: (relativePath: JSONPath) => boolean = expandSelf): Promise<void>
JSONEditor.prototype.expand(path: JSONPath, callback?: (relativePath: JSONPath) => boolean = expandSelf): void
```
Expand 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.
Expand All @@ -755,15 +755,15 @@ The library exports a couple of utility functions for commonly used `callback` f
### collapse
```ts
JSONEditor.prototype.collapse(path: JSONPath, recursive?: boolean = false): Promise<void>
JSONEditor.prototype.collapse(path: JSONPath, recursive?: boolean = false): 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
```ts
JSONEditor.prototype.transform({ id?: string, rootPath?: [], onTransform: ({ operations: JSONPatchDocument, json: unknown, transformedJson: unknown }) => void, onClose: () => void })
JSONEditor.prototype.transform(options?: { id?: string, rootPath?: [], onTransform: ({ operations: JSONPatchDocument, json: unknown, transformedJson: unknown }) => void, onClose: () => void })
```
Programmatically trigger clicking of the transform button in the main menu, opening the transform model. If a callback `onTransform` is provided, it will replace the build-in logic to apply a transform, allowing you to process the transform operations in an alternative way. If provided, `onClose` callback will trigger when the transform modal closes, both after the user clicked apply or cancel. If an `id` is provided, the transform modal will load the previous status of this `id` instead of the status of the editors transform modal.
Expand All @@ -774,7 +774,7 @@ Programmatically trigger clicking of the transform button in the main menu, open
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.
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
Expand All @@ -787,7 +787,7 @@ Find the DOM element of a given path. Returns `undefined` when not found.
#### acceptAutoRepair
```ts
JSONEditor.prototype.acceptAutoRepair(): Promise<Content>
JSONEditor.prototype.acceptAutoRepair(): Content
```
In tree mode, invalid JSON is automatically repaired when loaded. When the repair was successful, the repaired contents are rendered but not yet applied to the document itself until the user clicks "Ok" or starts editing the data. Instead of accepting the repair, the user can also click "Repair manually instead". Invoking `.acceptAutoRepair()` will programmatically accept the repair. This will trigger an update, and the method itself also returns the updated contents. In case of `text` mode or when the editor is not in an "accept auto repair" status, nothing will happen, and the contents will be returned as is.
Expand Down Expand Up @@ -819,7 +819,7 @@ Change the current selection. See also option `selection`.
#### focus
```ts
JSONEditor.prototype.focus(): Promise<void>
JSONEditor.prototype.focus(): void
```
Give the editor focus.
Expand Down
46 changes: 25 additions & 21 deletions src/lib/components/JSONEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import AbsolutePopup from './modals/popup/AbsolutePopup.svelte'
import { jsonQueryLanguage } from '$lib/plugins/query/jsonQueryLanguage.js'
import { renderValue } from '$lib/plugins/value/renderValue.js'
import { tick } from 'svelte'
import { flushSync } from 'svelte'
import TransformModal from './modals/TransformModal.svelte'
import type {
Content,
Expand Down Expand Up @@ -171,7 +171,7 @@
return content
}
export async function set(newContent: Content): Promise<void> {
export function set(newContent: Content): void {
debug('set')
const contentError = validateContentType(newContent)
Expand All @@ -184,9 +184,11 @@
// update content *after* re-render, so that the new editor will trigger an onChange event
content = newContent
flushSync()
}
export async function update(updatedContent: Content): Promise<void> {
export function update(updatedContent: Content): void {
debug('update')
const contentError = validateContentType(updatedContent)
Expand All @@ -196,42 +198,44 @@
content = updatedContent
await tick() // await rerender
flushSync()
}
export async function patch(operations: JSONPatchDocument): Promise<JSONPatchResult> {
export function patch(operations: JSONPatchDocument): JSONPatchResult {
// Note that patch has an optional afterPatch callback.
// right now we don's support this in the public API.
const result = refJSONEditorRoot.patch(operations)
await tick() // await rerender
flushSync()
return result
}
export async function select(newSelection: JSONEditorSelection | undefined) {
export function select(newSelection: JSONEditorSelection | undefined): void {
selection = newSelection
await tick() // await rerender
flushSync()
}
export async function expand(path: JSONPath, callback?: OnExpand): Promise<void> {
export function expand(path: JSONPath, callback?: OnExpand): void {
refJSONEditorRoot.expand(path, callback)
await tick() // await rerender
flushSync()
}
export async function collapse(path: JSONPath, recursive = false): Promise<void> {
export function collapse(path: JSONPath, recursive = false): void {
refJSONEditorRoot.collapse(path, recursive)
await tick() // await rerender
flushSync()
}
/**
* Open the transform modal
*/
export function transform(options: TransformModalOptions): void {
export function transform(options: TransformModalOptions = {}): void {
refJSONEditorRoot.transform(options)
flushSync()
}
/**
Expand All @@ -253,10 +257,10 @@
* mode or when the editor is not in an "accept auto repair" status, nothing
* will happen, and the contents will be returned as is.
*/
export async function acceptAutoRepair(): Promise<Content> {
export function acceptAutoRepair(): Content {
const content = refJSONEditorRoot.acceptAutoRepair()
await tick() // await rerender
flushSync()
return content
}
Expand All @@ -269,17 +273,17 @@
return refJSONEditorRoot.findElement(path)
}
export async function focus(): Promise<void> {
export function focus(): void {
refJSONEditorRoot.focus()
await tick() // await rerender
flushSync()
}
export async function refresh(): Promise<void> {
await refJSONEditorRoot.refresh()
}
export async function updateProps(props: JSONEditorPropsOptional): Promise<void> {
export function updateProps(props: JSONEditorPropsOptional): void {
const names = Object.keys(props) as (keyof JSONEditorPropsOptional)[]
for (const name of names) {
Expand Down Expand Up @@ -389,7 +393,7 @@
debug(`Unknown property "${name}"`)
}
await tick() // await rerender
flushSync()
}
export async function destroy() {
Expand Down Expand Up @@ -436,8 +440,8 @@
mode = newMode
await tick()
await focus()
flushSync()
focus()
onChangeMode(newMode)
}
Expand Down
8 changes: 4 additions & 4 deletions src/lib/components/controls/SearchBox.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
updateSearchResult
} from '$lib/logic/search.js'
import type { JSONPath } from 'immutable-json-patch'
import { tick } from 'svelte'
import { flushSync } from 'svelte'
const debug = createDebug('jsoneditor:SearchBox')
Expand Down Expand Up @@ -125,8 +125,8 @@
}
async function handlePaste() {
await tick()
setTimeout(() => applyChangedSearchTextDebounced.flush())
flushSync()
await applyChangedSearchTextDebounced.flush()
}
async function handleReplace() {
Expand Down Expand Up @@ -161,7 +161,7 @@
}))
// immediately trigger updating the search results
await tick()
flushSync()
await applyChangedJsonDebounced.flush()
// focus to the next search result
Expand Down
15 changes: 8 additions & 7 deletions src/lib/components/modals/JSONEditorModal.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<svelte:options immutable={true} />

<script lang="ts">
import { onMount, tick } from 'svelte'
import { flushSync, onMount } from 'svelte'
import Header from './Header.svelte'
import type { JSONPatchDocument, JSONPath } from 'immutable-json-patch'
import { compileJSONPointer, immutableJSONPatch, isJSONArray } from 'immutable-json-patch'
Expand Down Expand Up @@ -138,7 +138,8 @@
const parentState = stack[stack.length - 2] || rootState
const updatedParentState: ModalState = { ...parentState, content: updatedParentContent }
stack = [...stack.slice(0, stack.length - 2), updatedParentState]
tick().then(scrollToSelection)
flushSync()
scrollToSelection()
} else {
onPatch(operations)
Expand All @@ -158,10 +159,9 @@
} else if (stack.length > 1) {
// remove the last item from the stack
stack = initial(stack)
tick().then(() => {
refEditor?.focus()
scrollToSelection()
})
flushSync()
refEditor?.focus()
scrollToSelection()
// clear any error from the just closed state
error = undefined
Expand Down Expand Up @@ -208,7 +208,8 @@
}
stack = [...stack, nestedModalState]
tick().then(() => refEditor?.focus())
flushSync()
refEditor?.focus()
}
function focus(element: HTMLElement) {
Expand Down
16 changes: 8 additions & 8 deletions src/lib/components/modes/tablemode/TableMode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
import { sortJson } from '$lib/logic/sort.js'
import { keyComboFromEvent } from '$lib/utils/keyBindings.js'
import { createFocusTracker } from '$lib/components/controls/createFocusTracker.js'
import { getContext, onDestroy, onMount, tick } from 'svelte'
import { flushSync, getContext, onDestroy, onMount } from 'svelte'
import { jsonrepair } from 'jsonrepair'
import Message from '../../controls/Message.svelte'
import { faCheck, faCode, faWrench } from '@fortawesome/free-solid-svg-icons'
Expand Down Expand Up @@ -840,9 +840,9 @@
container: refContents,
offset,
duration: SCROLL_DURATION,
callback: async () => {
callback: () => {
// ensure the element is rendered now that it is scrolled into view
await tick()
flushSync()
// TODO: improve horizontal scrolling: animate and integrate with the vertical scrolling (jump)
scrollToHorizontal(path)
Expand Down Expand Up @@ -1593,11 +1593,11 @@
showSearch = false
showReplace = false
tick().then(() => {
// trick to make sure the focus goes to the search box
showSearch = true
showReplace = findAndReplace
})
flushSync()
// trick to make sure the focus goes to the search box
showSearch = true
showReplace = findAndReplace
}
function handleUndo() {
Expand Down
12 changes: 9 additions & 3 deletions src/lib/components/modes/textmode/TextMode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import { immutableJSONPatch, revertJSONPatch } from 'immutable-json-patch'
import { jsonrepair } from 'jsonrepair'
import { debounce, isEqual, uniqueId } from 'lodash-es'
import { onDestroy, onMount, tick } from 'svelte'
import { flushSync, onDestroy, onMount } from 'svelte'
import {
JSON_STATUS_INVALID,
JSON_STATUS_REPAIRABLE,
Expand Down Expand Up @@ -901,7 +901,8 @@
// This change will be dispatched by Svelte on the next tick. Before
// that tick, emitOnSelect would be fired based on the "old" contents,
// which may be out of range when the replacement by the user is shorter.
tick().then(emitOnSelect)
flushSync()
emitOnSelect()
}
function updateLinter(validator: Validator | undefined) {
Expand Down Expand Up @@ -949,7 +950,7 @@
async function updateTheme(): Promise<void> {
// we check the theme on the next tick, to make sure the page
// is re-rendered with (possibly) changed CSS variables
await tick()
flushSync()
if (codeMirrorView) {
const dark = hasDarkTheme()
Expand All @@ -958,7 +959,12 @@
codeMirrorView.dispatch({
effects: [themeCompartment.reconfigure(EditorView.theme({}, { dark }))]
})
// resolve on next tick, when code mirror rendering is updated
return new Promise((resolve) => setTimeout(resolve))
}
return Promise.resolve()
}
function createIndent(indentation: number | string): Extension[] {
Expand Down
Loading

0 comments on commit 6dd69cd

Please sign in to comment.