Skip to content

Commit

Permalink
feat: implement onRenderContextMenu (fix #82)
Browse files Browse the repository at this point in the history
  • Loading branch information
josdejong committed Dec 8, 2023
1 parent a37f462 commit e53a620
Show file tree
Hide file tree
Showing 16 changed files with 274 additions and 73 deletions.
138 changes: 99 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,58 +277,118 @@ const editor = new JSONEditor({

To adjust the text color of keys or values, the color of the classes `.jse-key` and `.jse-value` can be overwritten.

- `onRenderValue(props: RenderValueProps) : RenderValueComponentDescription[]`
- `onRenderValue(props: RenderValueProps) : RenderValueComponentDescription[]`

_EXPERIMENTAL! This API will most likely change in future versions._
_EXPERIMENTAL! This API will most likely change in future versions._

Customize rendering of the values. By default, `renderValue` is used, which renders a value as an editable div and depending on the value can also render a boolean toggle, a color picker, and a timestamp tag. Multiple components can be rendered alongside each other, like the boolean toggle and color picker being rendered left from the editable div. Built in value renderer components: `EditableValue`, `ReadonlyValue`, `BooleanToggle`, `ColorPicker`, `TimestampTag`, `EnumValue`.
Customize rendering of the values. By default, `renderValue` is used, which renders a value as an editable div and depending on the value can also render a boolean toggle, a color picker, and a timestamp tag. Multiple components can be rendered alongside each other, like the boolean toggle and color picker being rendered left from the editable div. Built in value renderer components: `EditableValue`, `ReadonlyValue`, `BooleanToggle`, `ColorPicker`, `TimestampTag`, `EnumValue`.

For JSON Schema enums, there is a value renderer `renderJSONSchemaEnum` which renders enums using the `EnumValue` component. This can be used like:
For JSON Schema enums, there is a value renderer `renderJSONSchemaEnum` which renders enums using the `EnumValue` component. This can be used like:

```js
import { renderJSONSchemaEnum, renderValue } from 'svelte-jsoneditor'
```js
import { renderJSONSchemaEnum, renderValue } from 'svelte-jsoneditor'
function onRenderValue(props) {
// use the enum renderer, and fallback on the default renderer
return renderJSONSchemaEnum(props, schema, schemaDefinitions) || renderValue(props)
}
```
function onRenderValue(props) {
// use the enum renderer, and fallback on the default renderer
return renderJSONSchemaEnum(props, schema, schemaDefinitions) || renderValue(props)
}
```

- `onRenderMenu(items: MenuItem[], context: { mode: 'tree' | 'text' | 'table', modal: boolean }) : MenuItem[] | undefined`.
Callback which can be used to make changes to the 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. Using the context values `mode` and `modal`, different actions can be taken depending on the mode of the editor and whether the editor is rendered inside a modal or not.
- `onRenderMenu(items: MenuItem[], context: { mode: 'tree' | 'text' | 'table', modal: boolean }) : MenuItem[] | undefined`.
Callback which can be used to make changes to the 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. Using the context values `mode` and `modal`, different actions can be taken depending on the mode of the editor and whether the editor is rendered inside a modal or not.

A menu item `MenuItem` can be one of the following types:
A menu item `MenuItem` can be one of the following types:

- Button:
- Button:

```ts
interface MenuButtonItem {
onClick: () => void
icon?: IconDefinition
text?: string
title?: string
className?: string
disabled?: boolean
}
```
```ts
interface MenuButton {
type: 'button'
onClick: () => void
icon?: IconDefinition
text?: string
title?: string
className?: string
disabled?: boolean
}
```

- Separator (gray vertical line between a group of items):
- Separator (gray vertical line between a group of items):

```ts
interface MenuSeparatorItem {
separator: true
}
```
```ts
interface MenuSeparator {
type: 'separator'
}
```

- Space (fills up empty space):
- Space (fills up empty space):

```ts
interface MenuSpaceItem {
space: true
}
```
```ts
interface MenuSpace {
type: 'space'
}
```

- `onRenderContextMenu(items: ContextMenuItem[], context: { mode: 'tree' | 'text' | 'table', modal: boolean }) : ContextMenuItem[] | 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. Using the context values `mode` and `modal`, different actions can be taken depending on the mode of the editor and whether the editor is rendered inside a modal or not.

A menu item `ContextMenuItem` can be one of the following types:

- Button:

```ts
interface MenuButton {
type: 'button'
onClick: () => void
icon?: IconDefinition
text?: string
title?: string
className?: string
disabled?: boolean
}
```

- Dropdown button:

```ts
interface MenuDropDownButton {
type: 'dropdown-button'
main: MenuButton
width?: string
items: MenuButton[]
}
```

- Separator (gray line between a group of items):

```ts
interface MenuSeparator {
type: 'separator'
}
```

- Menu row and column:

```ts
interface MenuLabel {
type: 'label'
text: string
}
interface ContextMenuColumn {
type: 'column'
items: Array<MenuButton | MenuDropDownButton | MenuLabel | MenuSeparator>
}
interface ContextMenuRow {
type: 'row'
items: Array<MenuButton | MenuDropDownButton | ContextMenuColumn>
}
```

- `onSelect: (selection: JSONEditorSelection | null) => void`

Expand Down
4 changes: 4 additions & 0 deletions src/lib/components/JSONEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import type { Callbacks, Component } from 'svelte-simple-modal/types/Modal.svelte'
import ModalRef from '../components/modals/ModalRef.svelte'
import type { Open } from 'svelte-simple-modal/types/Modal.svelte.js'
import type { OnRenderContextMenu } from '$lib'
// TODO: document how to enable debugging in the readme: localStorage.debug="jsoneditor:*", then reload
const debug = createDebug('jsoneditor:JSONEditor')
Expand Down Expand Up @@ -93,6 +94,7 @@
export let onRenderValue: OnRenderValue = renderValue
export let onClassName: OnClassName = () => undefined
export let onRenderMenu: OnRenderMenu = noop
export let onRenderContextMenu: OnRenderContextMenu = noop
export let onChangeMode: OnChangeMode = noop
export let onError: OnError = (err) => {
console.error(err)
Expand Down Expand Up @@ -407,6 +409,7 @@
onRenderValue,
onClassName,
onRenderMenu,
onRenderContextMenu,
onSortModal,
onTransformModal
}),
Expand Down Expand Up @@ -475,6 +478,7 @@
onFocus={handleFocus}
onBlur={handleBlur}
{onRenderMenu}
{onRenderContextMenu}
{onSortModal}
{onTransformModal}
{onJSONEditorModal}
Expand Down
3 changes: 3 additions & 0 deletions src/lib/components/modals/JSONEditorModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
JSONPathParser,
OnClassName,
OnPatch,
OnRenderContextMenu,
OnRenderMenu,
OnRenderValue,
OnSortModal,
Expand Down Expand Up @@ -57,6 +58,7 @@
export let onRenderValue: OnRenderValue
export let onClassName: OnClassName
export let onRenderMenu: OnRenderMenu
export let onRenderContextMenu: OnRenderContextMenu
export let onSortModal: OnSortModal
export let onTransformModal: OnTransformModal
Expand Down Expand Up @@ -260,6 +262,7 @@
onFocus={noop}
onBlur={noop}
{onRenderMenu}
{onRenderContextMenu}
{onSortModal}
{onTransformModal}
onJSONEditorModal={handleJSONEditorModal}
Expand Down
15 changes: 13 additions & 2 deletions src/lib/components/modes/JSONEditorRoot.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import type {
Content,
ContentErrors,
ContextMenuItem,
JSONEditorSelection,
JSONParser,
JSONPatchResult,
Expand All @@ -16,8 +17,10 @@
OnExpand,
OnFocus,
OnJSONEditorModal,
OnRenderContextMenu,
OnRenderContextMenuInternal,
OnRenderMenu,
OnRenderMenuWithoutContext,
OnRenderMenuInternal,
OnRenderValue,
OnSelect,
OnSortModal,
Expand Down Expand Up @@ -59,6 +62,7 @@
export let onRenderValue: OnRenderValue
export let onClassName: OnClassName
export let onRenderMenu: OnRenderMenu
export let onRenderContextMenu: OnRenderContextMenu
export let onError: OnError
export let onFocus: OnFocus
export let onBlur: OnBlur
Expand Down Expand Up @@ -102,7 +106,7 @@
type: 'separator'
}
let handleRenderMenu: OnRenderMenuWithoutContext
let handleRenderMenu: OnRenderMenuInternal
$: handleRenderMenu = (items: MenuItem[]) => {
const updatedItems = isMenuSpace(items[0])
? modeMenuItems.concat(items) // menu is empty, readOnly mode
Expand All @@ -111,6 +115,11 @@
return onRenderMenu(updatedItems, { mode, modal: insideModal }) || updatedItems
}
let handleRenderContextMenu: OnRenderContextMenuInternal
$: handleRenderContextMenu = (items: ContextMenuItem[]) => {
return onRenderContextMenu(items, { mode, modal: insideModal }) || items
}
export function patch(operations: JSONPatchDocument): JSONPatchResult {
if (refTreeMode) {
// Note that tree mode has an optional afterPatch callback.
Expand Down Expand Up @@ -277,6 +286,7 @@
{onFocus}
{onBlur}
onRenderMenu={handleRenderMenu}
onRenderContextMenu={handleRenderContextMenu}
{onSortModal}
{onTransformModal}
{onJSONEditorModal}
Expand Down Expand Up @@ -307,6 +317,7 @@
{onFocus}
{onBlur}
onRenderMenu={handleRenderMenu}
onRenderContextMenu={handleRenderContextMenu}
{onSortModal}
{onTransformModal}
{onJSONEditorModal}
Expand Down
7 changes: 5 additions & 2 deletions src/lib/components/modes/tablemode/TableMode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
OnChangeMode,
OnFocus,
OnJSONEditorModal,
OnRenderMenuWithoutContext,
OnRenderContextMenuInternal,
OnRenderMenuInternal,
OnRenderValue,
OnSelect,
OnSortModal,
Expand Down Expand Up @@ -168,7 +169,8 @@
export let onChangeMode: OnChangeMode
export let onSelect: OnSelect
export let onRenderValue: OnRenderValue
export let onRenderMenu: OnRenderMenuWithoutContext
export let onRenderMenu: OnRenderMenuInternal
export let onRenderContextMenu: OnRenderContextMenuInternal
export let onFocus: OnFocus
export let onBlur: OnBlur
export let onSortModal: OnSortModal
Expand Down Expand Up @@ -941,6 +943,7 @@
onInsertAfterRow: handleInsertAfterRow,
onRemoveRow: handleRemoveRow,
onRenderContextMenu,
onCloseContextMenu: function () {
closeAbsolutePopup(popupId)
focus()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@
} from '$lib/logic/selection.js'
import { isObjectOrArray } from '$lib/utils/typeUtils.js'
import { faCheckSquare, faSquare } from '@fortawesome/free-regular-svg-icons'
import type { ContextMenuItem, DocumentState, JSONParser } from '$lib/types'
import type {
ContextMenuItem,
DocumentState,
JSONParser,
OnRenderContextMenuInternal
} from '$lib/types'
import { getEnforceString } from '$lib/logic/documentState.js'
import ContextMenu from '../../../../components/controls/contextmenu/ContextMenu.svelte'
Expand All @@ -32,6 +37,7 @@
export let showTip: boolean
export let onCloseContextMenu: () => void
export let onRenderContextMenu: OnRenderContextMenuInternal
export let onEditValue: () => void
export let onEditRow: () => void
export let onToggleEnforceString: () => void
Expand Down Expand Up @@ -132,8 +138,8 @@
onRemoveRow()
}
let items: ContextMenuItem[]
$: items = [
let defaultItems: ContextMenuItem[]
$: defaultItems = [
{ type: 'separator' },
{
type: 'row',
Expand Down Expand Up @@ -299,6 +305,8 @@
]
}
]
$: items = onRenderContextMenu(defaultItems)
</script>

<ContextMenu
Expand Down
6 changes: 3 additions & 3 deletions src/lib/components/modes/tablemode/menu/TableMenu.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<svelte:options immutable={true} />

<script lang="ts">
import type { MenuItem, OnRenderMenuWithoutContext } from '$lib/types'
import type { MenuItem, OnRenderMenuInternal } from '$lib/types'
import Menu from '../../../controls/Menu.svelte'
import {
faEllipsisV,
Expand All @@ -22,7 +22,7 @@
export let onContextMenu: () => void
export let onUndo: () => void
export let onRedo: () => void
export let onRenderMenu: OnRenderMenuWithoutContext
export let onRenderMenu: OnRenderMenuInternal
let defaultItems: MenuItem[]
$: defaultItems = !readOnly
Expand Down Expand Up @@ -80,7 +80,7 @@
]
let items: MenuItem[]
$: items = onRenderMenu(defaultItems) || defaultItems
$: items = onRenderMenu(defaultItems)
</script>

<Menu {items} />
4 changes: 2 additions & 2 deletions src/lib/components/modes/textmode/TextMode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
OnChangeMode,
OnError,
OnFocus,
OnRenderMenuWithoutContext,
OnRenderMenuInternal,
OnSelect,
OnSortModal,
OnTransformModal,
Expand Down Expand Up @@ -134,7 +134,7 @@
export let onError: OnError
export let onFocus: OnFocus
export let onBlur: OnBlur
export let onRenderMenu: OnRenderMenuWithoutContext
export let onRenderMenu: OnRenderMenuInternal
export let onSortModal: OnSortModal
export let onTransformModal: OnTransformModal
Expand Down
Loading

0 comments on commit e53a620

Please sign in to comment.