Skip to content

Commit db3fb57

Browse files
authoredMar 8, 2024··
feat: #399 enable onRenderContextMenu when the editor is readOnly (#411)
BREAKING CHANGE: The `onRenderContextMenu` callback now also triggers when the editor is `readOnly`, so you now have to handle that case in the callback.
1 parent dca87f0 commit db3fb57

File tree

8 files changed

+213
-198
lines changed

8 files changed

+213
-198
lines changed
 

‎README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -517,10 +517,10 @@ A menu item `MenuItem` can be one of the following types:
517517
#### onRenderContextMenu
518518
519519
```ts
520-
onRenderContextMenu(items: ContextMenuItem[], context: { mode: 'tree' | 'text' | 'table', modal: boolean, selection: JSONEditorSelection | null }) : ContextMenuItem[] | undefined
520+
onRenderContextMenu(items: ContextMenuItem[], context: { mode: 'tree' | 'text' | 'table', modal: boolean, selection: JSONEditorSelection | null }) : ContextMenuItem[] | false | undefined
521521
```
522522
523-
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`, `modal` and `selection`, different actions can be taken depending on the mode of the editor, whether the editor is rendered inside a modal or not and the path of selection.
523+
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. Using the context values `mode`, `modal` and `selection`, different actions can be taken depending on the mode of the editor, whether the editor is rendered inside a modal or not and the path of selection.
524524
525525
A menu item `ContextMenuItem` can be one of the following types:
526526

‎src/lib/components/modes/JSONEditorRoot.svelte

+4-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,10 @@
122122
$: handleRenderContextMenu = (items: ContextMenuItem[]) => {
123123
const itemsOriginal = cloneDeep(items) // the user may change items in the callback
124124
125-
return onRenderContextMenu(items, { mode, modal: insideModal, selection }) || itemsOriginal
125+
return (
126+
onRenderContextMenu(items, { mode, modal: insideModal, selection }) ??
127+
(readOnly ? false : itemsOriginal)
128+
)
126129
}
127130
128131
export function patch(operations: JSONPatchDocument): JSONPatchResult {

‎src/lib/components/modes/tablemode/TableMode.svelte

+20-12
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,15 @@
135135
} from '$lib/logic/actions.js'
136136
import JSONRepairModal from '../../modals/JSONRepairModal.svelte'
137137
import { resizeObserver } from '$lib/actions/resizeObserver.js'
138-
import TableContextMenu from '../../../components/modes/tablemode/contextmenu/TableContextMenu.svelte'
139138
import CopyPasteModal from '../../../components/modals/CopyPasteModal.svelte'
140139
import ContextMenuPointer from '../../../components/controls/contextmenu/ContextMenuPointer.svelte'
141140
import TableModeWelcome from './TableModeWelcome.svelte'
142141
import JSONPreview from '../../controls/JSONPreview.svelte'
143142
import RefreshColumnHeader from './RefreshColumnHeader.svelte'
144143
import type { Context } from 'svelte-simple-modal'
144+
import type { ContextMenuItem } from '$lib/types'
145+
import createTableContextMenuItems from './contextmenu/createTableContextMenuItems'
146+
import ContextMenu from '../../controls/contextmenu/ContextMenu.svelte'
145147
146148
const debug = createDebug('jsoneditor:TableMode')
147149
const { open } = getContext<Context>('simple-modal')
@@ -921,11 +923,10 @@
921923
offsetLeft,
922924
showTip
923925
}: AbsolutePopupOptions) {
924-
const props = {
926+
const defaultItems: ContextMenuItem[] = createTableContextMenuItems({
925927
json,
926-
documentState: documentState,
928+
documentState,
927929
parser,
928-
showTip,
929930
930931
onEditValue: handleEditValue,
931932
onEditRow: handleEditRow,
@@ -937,9 +938,20 @@
937938
onDuplicateRow: handleDuplicateRow,
938939
onInsertBeforeRow: handleInsertBeforeRow,
939940
onInsertAfterRow: handleInsertAfterRow,
940-
onRemoveRow: handleRemoveRow,
941+
onRemoveRow: handleRemoveRow
942+
})
943+
944+
const items = onRenderContextMenu(defaultItems)
945+
946+
if (items === false) {
947+
return
948+
}
941949
942-
onRenderContextMenu,
950+
const props = {
951+
tip: showTip
952+
? 'Tip: you can open this context menu via right-click or with Ctrl+Q'
953+
: undefined,
954+
items,
943955
onCloseContextMenu: function () {
944956
closeAbsolutePopup(popupId)
945957
focus()
@@ -948,7 +960,7 @@
948960
949961
modalOpen = true
950962
951-
const popupId = openAbsolutePopup(TableContextMenu, props, {
963+
const popupId = openAbsolutePopup(ContextMenu, props, {
952964
left,
953965
top,
954966
offsetTop,
@@ -965,7 +977,7 @@
965977
}
966978
967979
function handleContextMenu(event: Event) {
968-
if (readOnly || isEditingSelection(documentState.selection)) {
980+
if (isEditingSelection(documentState.selection)) {
969981
return
970982
}
971983
@@ -1014,10 +1026,6 @@
10141026
}
10151027
10161028
function handleContextMenuFromTableMenu(event: MouseEvent) {
1017-
if (readOnly) {
1018-
return
1019-
}
1020-
10211029
openContextMenu({
10221030
anchor: findParentWithNodeName(event.target as HTMLElement, 'BUTTON'),
10231031
offsetTop: 0,

‎src/lib/components/modes/tablemode/contextmenu/TableContextMenu.svelte ‎src/lib/components/modes/tablemode/contextmenu/createTableContextMenuItems.ts

+59-69
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,67 @@
1-
<svelte:options immutable={true} />
1+
import type { ContextMenuItem, DocumentState, JSONParser } from 'svelte-jsoneditor'
2+
import {
3+
faCheckSquare,
4+
faClone,
5+
faCopy,
6+
faCut,
7+
faPaste,
8+
faPen,
9+
faPlus,
10+
faSquare,
11+
faTrashCan
12+
} from '@fortawesome/free-solid-svg-icons'
13+
import { isKeySelection, isMultiSelection, isValueSelection } from 'svelte-jsoneditor'
14+
import { compileJSONPointer, getIn } from 'immutable-json-patch'
15+
import { getFocusPath, singleItemSelected } from '$lib/logic/selection'
16+
import { isObjectOrArray } from '$lib/utils/typeUtils'
17+
import { getEnforceString } from '$lib/logic/documentState'
218

3-
<script lang="ts">
4-
import {
5-
faClone,
6-
faCopy,
7-
faCut,
8-
faPaste,
9-
faPen,
10-
faPlus,
11-
faTrashCan
12-
} from '@fortawesome/free-solid-svg-icons'
13-
import { compileJSONPointer, getIn } from 'immutable-json-patch'
14-
import {
15-
getFocusPath,
16-
isKeySelection,
17-
isMultiSelection,
18-
isValueSelection,
19-
singleItemSelected
20-
} from '$lib/logic/selection.js'
21-
import { isObjectOrArray } from '$lib/utils/typeUtils.js'
22-
import { faCheckSquare, faSquare } from '@fortawesome/free-regular-svg-icons'
23-
import type {
24-
ContextMenuItem,
25-
DocumentState,
26-
JSONParser,
27-
OnRenderContextMenuInternal
28-
} from '$lib/types'
29-
import { getEnforceString } from '$lib/logic/documentState.js'
30-
import ContextMenu from '../../../../components/controls/contextmenu/ContextMenu.svelte'
19+
export default function ({
20+
json,
21+
documentState,
22+
parser,
23+
onEditValue,
24+
onEditRow,
25+
onToggleEnforceString,
26+
onCut,
27+
onCopy,
28+
onPaste,
29+
onRemove,
30+
onDuplicateRow,
31+
onInsertBeforeRow,
32+
onInsertAfterRow,
33+
onRemoveRow
34+
}: {
35+
json: unknown | undefined
36+
documentState: DocumentState
37+
parser: JSONParser
38+
onEditValue: () => void
39+
onEditRow: () => void
40+
onToggleEnforceString: () => void
41+
onCut: (indent: boolean) => void
42+
onCopy: (indent: boolean) => void
43+
onPaste: () => void
44+
onRemove: () => void
45+
onDuplicateRow: () => void
46+
onInsertBeforeRow: () => void
47+
onInsertAfterRow: () => void
48+
onRemoveRow: () => void
49+
}): ContextMenuItem[] {
50+
const selection = documentState.selection
3151

32-
export let json: unknown | undefined
33-
export let documentState: DocumentState
34-
export let parser: JSONParser
52+
const hasJson = json !== undefined
53+
const hasSelection = !!selection
54+
const focusValue =
55+
json !== undefined && selection ? getIn(json, getFocusPath(selection)) : undefined
3556

36-
export let showTip: boolean
37-
38-
export let onCloseContextMenu: () => void
39-
export let onRenderContextMenu: OnRenderContextMenuInternal
40-
export let onEditValue: () => void
41-
export let onEditRow: () => void
42-
export let onToggleEnforceString: () => void
43-
export let onCut: (indent: boolean) => void
44-
export let onCopy: (indent: boolean) => void
45-
export let onPaste: () => void
46-
export let onRemove: () => void
47-
export let onDuplicateRow: () => void
48-
export let onInsertBeforeRow: () => void
49-
export let onInsertAfterRow: () => void
50-
export let onRemoveRow: () => void
51-
52-
$: selection = documentState.selection
53-
54-
$: hasJson = json !== undefined
55-
$: hasSelection = !!selection
56-
$: focusValue = json !== undefined && selection ? getIn(json, getFocusPath(selection)) : undefined
57-
58-
$: hasSelectionContents =
57+
const hasSelectionContents =
5958
hasJson &&
6059
(isMultiSelection(selection) || isKeySelection(selection) || isValueSelection(selection))
6160

62-
$: canEditValue = hasJson && selection != null && singleItemSelected(selection)
63-
$: canEnforceString = canEditValue && !isObjectOrArray(focusValue)
61+
const canEditValue = hasJson && selection != null && singleItemSelected(selection)
62+
const canEnforceString = canEditValue && !isObjectOrArray(focusValue)
6463

65-
$: enforceString =
64+
const enforceString =
6665
selection != null && focusValue !== undefined
6766
? getEnforceString(
6867
focusValue,
@@ -72,8 +71,7 @@
7271
)
7372
: false
7473

75-
let defaultItems: ContextMenuItem[]
76-
$: defaultItems = [
74+
return [
7775
{ type: 'separator' },
7876
{
7977
type: 'row',
@@ -239,12 +237,4 @@
239237
]
240238
}
241239
]
242-
243-
$: items = onRenderContextMenu(defaultItems)
244-
</script>
245-
246-
<ContextMenu
247-
{items}
248-
{onCloseContextMenu}
249-
tip={showTip ? 'Tip: you can open this context menu via right-click or with Ctrl+Q' : undefined}
250-
/>
240+
}

0 commit comments

Comments
 (0)
Please sign in to comment.