Skip to content

Commit

Permalink
feat: refactor the API to use undefined instead of null (#453)
Browse files Browse the repository at this point in the history
BREAKING CHANGE:

Changed the API to consistently use `undefined` instead of `null`. This involves properties `selection`, `onChange` (properties `contentErrors and `patchResult`), `onRenderContextMenu` (property `selection`), `onSelect`, and methods `validate`, and `select`.
  • Loading branch information
josdejong authored Jun 17, 2024
1 parent ed66b0b commit 3a9d51b
Show file tree
Hide file tree
Showing 56 changed files with 459 additions and 414 deletions.
8 changes: 8 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@
"sourceType": "module",
"ecmaVersion": 2019
},
"rules": {
"consistent-return": [
"warn",
{
"treatUndefinedAsUnspecified": false
}
]
},
"overrides": [
{
"files": ["*.svelte"],
Expand Down
6 changes: 3 additions & 3 deletions README-VANILLA.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A web-based tool to view, edit, format, transform, and validate JSON.

Try it out: https://jsoneditoronline.org
Try it out: <https://jsoneditoronline.org>

This is the vanilla variant of `svelte-jsoneditor`, which can be used in vanilla JavaScript or frameworks like SolidJS, React, Vue, Angular.

Expand Down Expand Up @@ -50,7 +50,7 @@ import { JSONEditor } from 'vanilla-jsoneditor/standalone.js'

The standalone bundle contains all dependencies of `vanilla-jsoneditor`, for example `lodash-es` and `Ajv`. If you use some of these dependencies in your project too, it means that they will be bundled twice in your web application, leading to a needlessly large application size. In general, it is preferable to use the default `import { JSONEditor } from 'vanilla-jsoneditor'` so dependencies can be reused.

## Use (Browser example loading the ES module):
## Use (Browser example loading the ES module)

```html
<!doctype html>
Expand Down Expand Up @@ -100,7 +100,7 @@ The standalone bundle contains all dependencies of `vanilla-jsoneditor`, for exa

Depending on whether you are using JavaScript of TypeScript, create either a JSX or TSX file:

### TypeScript:
### TypeScript

```tsx
//
Expand Down
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A web-based tool to view, edit, format, transform, and validate JSON.

Try it out: https://jsoneditoronline.org
Try it out: <https://jsoneditoronline.org>

The library is written with Svelte, but can be used in plain JavaScript too and in any framework (SolidJS, React, Vue, Angular, etc).

Expand Down Expand Up @@ -41,13 +41,13 @@ npm install vanilla-jsoneditor
### Examples

- Svelte:
- Playground: https://www.sveltelab.dev/q1l38ztdys4at87
- Playground: <https://www.sveltelab.dev/q1l38ztdys4at87>
- Examples: [/src/routes/examples](/src/routes/examples)
- Plain JavaScript examples:
- Try it out: https://jsbin.com/gatibux/edit?html,output
- Try it out: <https://jsbin.com/gatibux/edit?html,output>
- Examples: [/examples/browser](/examples/browser)
- React example: https://codesandbox.io/s/svelte-jsoneditor-react-59wxz
- Vue example: https://codesandbox.io/s/svelte-jsoneditor-vue-toln3w
- React example: <https://codesandbox.io/s/svelte-jsoneditor-react-59wxz>
- Vue example: <https://codesandbox.io/s/svelte-jsoneditor-vue-toln3w>

### Svelte usage

Expand Down Expand Up @@ -229,7 +229,7 @@ Pass the JSON contents to be rendered in the JSONEditor. `Content` is an object
#### selection

```ts
selection: JSONEditorSelection | null
selection: JSONEditorSelection | undefined
```

The current selected contents. You can use two-way binding using `bind:selection`. The `tree` mode supports `MultiSelection`, `KeySelection`, `ValueSelection`, `InsideSelection`, or `AfterSelection`. The `table` mode supports `ValueSelection`, and `text` mode supports `TextSelection.`.
Expand Down Expand Up @@ -386,7 +386,7 @@ Callback fired when an error occurs. Default implementation is to log an error i
#### onChange

```ts
onChange(content: Content, previousContent: Content, changeStatus: { contentErrors: ContentErrors | null, patchResult: JSONPatchResult | null })
onChange(content: Content, previousContent: Content, changeStatus: { contentErrors: ContentErrors | undefined, patchResult: JSONPatchResult | undefined })
```

The callback which is invoked on every change of the contents made by the user from within the editor. It will not trigger on changes that are applied programmatically via methods like `.set()`, `.update()`, or `.patch()`.
Expand Down Expand Up @@ -519,7 +519,7 @@ A menu item `MenuItem` can be one of the following types:
#### onRenderContextMenu
```ts
onRenderContextMenu(items: ContextMenuItem[], context: { mode: 'tree' | 'text' | 'table', modal: boolean, readOnly: boolean, selection: JSONEditorSelection | null }) : ContextMenuItem[] | false | undefined
onRenderContextMenu(items: ContextMenuItem[], context: { mode: 'tree' | 'text' | 'table', modal: boolean, readOnly: boolean, selection: JSONEditorSelection | undefined }) : ContextMenuItem[] | false | undefined
```
Callback which can be used to make changes to the context menu items. New items can be added, or existing items can be removed or reorganized. When the function returns `undefined`, the original `items` will be applied and the context menu will be displayed when `readOnly` is `false`. When the function returns `false`, the context menu will never be displayed. The callback is triggered too when the editor is `readOnly`, and in most cases you want to return `false` then.
Expand Down Expand Up @@ -583,7 +583,7 @@ A menu item `ContextMenuItem` can be one of the following types:
#### onSelect
```ts
onSelect: (selection: JSONEditorSelection | null) => void
onSelect: (selection: JSONEditorSelection | undefined) => void
```
Callback invoked when the selection is changed. When the selection is removed, the callback is invoked with `undefined` as argument. In `text` mode, a `TextSelection` will be fired. In `tree` and `table` mode, a `JSONSelection` will be fired (which can be `MultiSelection`, `KeySelection`, `ValueSelection`, `InsideSelection`, or `AfterSelection`). Use typeguards like `isTextSelection` and `isValueSelection` to check what type the selection has.
Expand Down Expand Up @@ -764,15 +764,15 @@ Refresh rendering of the contents, for example after changing the font size. Thi
#### validate
```ts
JSONEditor.prototype.validate() : ContentErrors | null
JSONEditor.prototype.validate() : ContentErrors | undefined
```
Get all current parse errors and validation errors.
#### select
```ts
JSONEditor.prototype.select(newSelection: JSONEditorSelection | null)
JSONEditor.prototype.select(newSelection: JSONEditorSelection | undefined)
```
Change the current selection. See also option `selection`.
Expand Down Expand Up @@ -863,13 +863,13 @@ The library exports a set of utility functions. The exact definitions of those f
The TypeScript types (like `Content`, `JSONSelection`, and `JSONPatchOperation`) are defined in the following source file:
https://github.com/josdejong/svelte-jsoneditor/blob/main/src/lib/types.ts
<https://github.com/josdejong/svelte-jsoneditor/blob/main/src/lib/types.ts>
## Styling
The editor can be styled using the available CSS variables. A full list with all variables can be found here:
https://github.com/josdejong/svelte-jsoneditor/blob/main/src/lib/themes/defaults.scss
<https://github.com/josdejong/svelte-jsoneditor/blob/main/src/lib/themes/defaults.scss>
### Custom theme color
Expand Down
2 changes: 1 addition & 1 deletion src/lib/actions/onEscape.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function handleKeyDown(event: KeyboardEvent) {
* This is useful for example when opening a model on top of another modal:
* you only want the top modal to close on Escape, and not the second modal.
*/
export function onEscape(element: Element | null, callback: Callback) {
export function onEscape(element: Element | undefined, callback: Callback) {
if (isEmpty(callbacks)) {
window.addEventListener('keydown', handleKeyDown)
}
Expand Down
33 changes: 20 additions & 13 deletions src/lib/components/JSONEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
const debug = createDebug('jsoneditor:JSONEditor')
export let content: Content = { text: '' }
export let selection: JSONEditorSelection | null = null
export let selection: JSONEditorSelection | undefined = undefined
export let readOnly = false
export let indentation: number | string = 2
Expand All @@ -78,7 +78,7 @@
export let escapeUnicodeCharacters = false
export let flattenColumns = true
export let parser: JSONParser = JSON
export let validator: Validator | null = null
export let validator: Validator | undefined = undefined
export let validationParser: JSONParser = JSON
export let pathParser: JSONPathParser = {
parse: parseJSONPath,
Expand All @@ -89,8 +89,8 @@
export let queryLanguageId: string = queryLanguages[0].id
export let onChangeQueryLanguage: OnChangeQueryLanguage = noop
export let onChange: OnChange = null
export let onSelect: OnSelect | null = null
export let onChange: OnChange | undefined = undefined
export let onSelect: OnSelect | undefined = undefined
export let onRenderValue: OnRenderValue = renderValue
export let onClassName: OnClassName = () => undefined
export let onRenderMenu: OnRenderMenu = noop
Expand All @@ -107,10 +107,12 @@
let hasFocus = false
let refJSONEditorRoot: JSONEditorRoot
let open: Open // svelte-simple-modal context open(...)
let jsoneditorModalState: {
component: Component
callbacks: Partial<Callbacks>
} | null = null
let jsoneditorModalState:
| {
component: Component
callbacks: Partial<Callbacks>
}
| undefined = undefined
$: {
const contentError = validateContentType(content)
Expand All @@ -119,6 +121,11 @@
}
}
// backward compatibility warning since v1.0.0
$: if (selection === null) {
console.warn('selection is invalid: it is null but should be undefined')
}
// We memoize the last parse result for the case when the content is text and very large.
// In that case parsing takes a few seconds. When the user switches between tree and table mode,
// without having made a change, we do not want to parse the text again.
Expand Down Expand Up @@ -198,7 +205,7 @@
return result
}
export async function select(newSelection: JSONEditorSelection | null) {
export async function select(newSelection: JSONEditorSelection | undefined) {
selection = newSelection
await tick() // await rerender
Expand All @@ -221,7 +228,7 @@
* Validate the contents of the editor using the configured validator.
* Returns a parse error or a list with validation warnings
*/
export function validate(): ContentErrors | null {
export function validate(): ContentErrors | undefined {
return refJSONEditorRoot.validate()
}
Expand All @@ -248,7 +255,7 @@
await refJSONEditorRoot.scrollTo(path)
}
export function findElement(path: JSONPath): Element | null {
export function findElement(path: JSONPath): Element | undefined {
return refJSONEditorRoot.findElement(path)
}
Expand Down Expand Up @@ -286,7 +293,7 @@
}
}
function handleSelect(updatedSelection: JSONEditorSelection | null) {
function handleSelect(updatedSelection: JSONEditorSelection | undefined) {
selection = updatedSelection
if (onSelect) {
Expand Down Expand Up @@ -424,7 +431,7 @@
function closeJSONEditorModal() {
jsoneditorModalState?.callbacks?.onClose?.()
jsoneditorModalState = null
jsoneditorModalState = undefined
}
$: {
Expand Down
6 changes: 3 additions & 3 deletions src/lib/components/controls/SearchBox.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,13 @@
// we pass searchText and json as argument to trigger search when these variables change,
// via various listeners like applyChangedSearchText
async function applySearch(showSearch: boolean, text: string, json: unknown) {
async function applySearch(showSearch: boolean, text: string, json: unknown): Promise<void> {
if (!showSearch) {
if (searchResult) {
searchResult = undefined
}
return
return Promise.resolve()
}
debug('applySearch', { showSearch, text })
Expand All @@ -247,7 +247,7 @@
searchResult = undefined
}
return
return Promise.resolve()
}
appliedText = text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
export let onContextMenu: OnContextMenu
function handleClick(event: MouseEvent & { currentTarget: EventTarget & HTMLButtonElement }) {
let buttonElem: Element | null = event.target as HTMLButtonElement
let buttonElem: Element | undefined = event.target as HTMLButtonElement
while (buttonElem && buttonElem.nodeName !== 'BUTTON') {
buttonElem = buttonElem.parentNode as Element
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/controls/createFocusTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const debug = createDebug('jsoneditor:FocusTracker')
export interface FocusTrackerProps {
onMount: (callback: () => void) => void
onDestroy: (callback: () => void) => void
getWindow: () => Window | null
getWindow: () => Window | undefined
hasFocus: () => boolean
onFocus: () => void
onBlur: () => void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
const debug = createDebug('jsoneditor:NavigationBar')
export let json: unknown
export let selection: JSONSelection | null
export let selection: JSONSelection | undefined
export let onSelect: OnJSONSelect
export let onError: OnError
export let pathParser: JSONPathParser
Expand Down
12 changes: 6 additions & 6 deletions src/lib/components/modals/JSONEditorModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
export let escapeUnicodeCharacters: boolean
export let flattenColumns: boolean
export let parser: JSONParser
export let validator: Validator | null
export let validator: Validator | undefined
export let validationParser: JSONParser
export let pathParser: JSONPathParser
Expand All @@ -68,7 +68,7 @@
interface ModalState {
mode: Mode
content: Content
selection: JSONEditorSelection | null
selection: JSONEditorSelection | undefined
relativePath: JSONPath
}
Expand All @@ -78,7 +78,7 @@
const rootState: ModalState = {
mode: determineMode(content),
content,
selection: null,
selection: undefined,
relativePath: path
}
let stack: ModalState[] = [rootState]
Expand All @@ -97,7 +97,7 @@
}
function scrollToSelection() {
const selection: JSONEditorSelection | null = last(stack)?.selection || null
const selection: JSONEditorSelection | undefined = last(stack)?.selection
if (isJSONSelection(selection)) {
refEditor.scrollTo(getFocusPath(selection))
}
Expand Down Expand Up @@ -180,7 +180,7 @@
stack = [...initial(stack), updatedState]
}
function handleChangeSelection(newSelection: JSONEditorSelection | null) {
function handleChangeSelection(newSelection: JSONEditorSelection | undefined) {
debug('handleChangeSelection', newSelection)
const updatedState = {
Expand Down Expand Up @@ -213,7 +213,7 @@
const nestedModalState = {
mode: determineMode(content),
content,
selection: null,
selection: undefined,
relativePath: path
}
stack = [...stack, nestedModalState]
Expand Down
8 changes: 4 additions & 4 deletions src/lib/components/modals/TransformModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@
{#if showOriginal}
<TreeMode
externalContent={selectedContent}
externalSelection={null}
externalSelection={undefined}
readOnly={true}
mainMenuBar={false}
navigationBar={false}
Expand All @@ -308,7 +308,7 @@
onTransformModal={noop}
onJSONEditorModal={noop}
{onClassName}
validator={null}
validator={undefined}
{validationParser}
{pathParser}
/>
Expand All @@ -321,7 +321,7 @@
{#if !previewError}
<TreeMode
externalContent={previewContent}
externalSelection={null}
externalSelection={undefined}
readOnly={true}
mainMenuBar={false}
navigationBar={false}
Expand All @@ -343,7 +343,7 @@
onTransformModal={noop}
onJSONEditorModal={noop}
{onClassName}
validator={null}
validator={undefined}
{validationParser}
{pathParser}
/>
Expand Down
Loading

0 comments on commit 3a9d51b

Please sign in to comment.