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

feat: hide context menu when onRenderContextMenu return false #411

Merged
merged 5 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -517,10 +517,10 @@ A menu item `MenuItem` can be one of the following types:
#### onRenderContextMenu

```ts
onRenderContextMenu(items: ContextMenuItem[], context: { mode: 'tree' | 'text' | 'table', modal: boolean, selection: JSONEditorSelection | null }) : ContextMenuItem[] | undefined
onRenderContextMenu(items: ContextMenuItem[], context: { mode: 'tree' | 'text' | 'table', modal: boolean, selection: JSONEditorSelection | null }) : 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. 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.
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.

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

Expand Down
5 changes: 4 additions & 1 deletion src/lib/components/modes/JSONEditorRoot.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,10 @@
$: handleRenderContextMenu = (items: ContextMenuItem[]) => {
const itemsOriginal = cloneDeep(items) // the user may change items in the callback

return onRenderContextMenu(items, { mode, modal: insideModal, selection }) || itemsOriginal
return (
onRenderContextMenu(items, { mode, modal: insideModal, selection }) ??
(readOnly ? false : itemsOriginal)
)
}

export function patch(operations: JSONPatchDocument): JSONPatchResult {
Expand Down
32 changes: 20 additions & 12 deletions src/lib/components/modes/tablemode/TableMode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,15 @@
} from '$lib/logic/actions.js'
import JSONRepairModal from '../../modals/JSONRepairModal.svelte'
import { resizeObserver } from '$lib/actions/resizeObserver.js'
import TableContextMenu from '../../../components/modes/tablemode/contextmenu/TableContextMenu.svelte'
import CopyPasteModal from '../../../components/modals/CopyPasteModal.svelte'
import ContextMenuPointer from '../../../components/controls/contextmenu/ContextMenuPointer.svelte'
import TableModeWelcome from './TableModeWelcome.svelte'
import JSONPreview from '../../controls/JSONPreview.svelte'
import RefreshColumnHeader from './RefreshColumnHeader.svelte'
import type { Context } from 'svelte-simple-modal'
import type { ContextMenuItem } from '$lib/types'
import createTableContextMenuItems from './contextmenu/createTableContextMenuItems'
import ContextMenu from '../../controls/contextmenu/ContextMenu.svelte'

const debug = createDebug('jsoneditor:TableMode')
const { open } = getContext<Context>('simple-modal')
Expand Down Expand Up @@ -921,11 +923,10 @@
offsetLeft,
showTip
}: AbsolutePopupOptions) {
const props = {
const defaultItems: ContextMenuItem[] = createTableContextMenuItems({
json,
documentState: documentState,
documentState,
parser,
showTip,

onEditValue: handleEditValue,
onEditRow: handleEditRow,
Expand All @@ -937,9 +938,20 @@
onDuplicateRow: handleDuplicateRow,
onInsertBeforeRow: handleInsertBeforeRow,
onInsertAfterRow: handleInsertAfterRow,
onRemoveRow: handleRemoveRow,
onRemoveRow: handleRemoveRow
})

const items = onRenderContextMenu(defaultItems)

if (items === false) {
return
}

onRenderContextMenu,
const props = {
tip: showTip
? 'Tip: you can open this context menu via right-click or with Ctrl+Q'
: undefined,
items,
onCloseContextMenu: function () {
closeAbsolutePopup(popupId)
focus()
Expand All @@ -948,7 +960,7 @@

modalOpen = true

const popupId = openAbsolutePopup(TableContextMenu, props, {
const popupId = openAbsolutePopup(ContextMenu, props, {
left,
top,
offsetTop,
Expand All @@ -965,7 +977,7 @@
}

function handleContextMenu(event: Event) {
if (readOnly || isEditingSelection(documentState.selection)) {
if (isEditingSelection(documentState.selection)) {
return
}

Expand Down Expand Up @@ -1014,10 +1026,6 @@
}

function handleContextMenuFromTableMenu(event: MouseEvent) {
if (readOnly) {
return
}

openContextMenu({
anchor: findParentWithNodeName(event.target as HTMLElement, 'BUTTON'),
offsetTop: 0,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,68 +1,67 @@
<svelte:options immutable={true} />
import type { ContextMenuItem, DocumentState, JSONParser } from 'svelte-jsoneditor'
import {
faCheckSquare,
faClone,
faCopy,
faCut,
faPaste,
faPen,
faPlus,
faSquare,
faTrashCan
} from '@fortawesome/free-solid-svg-icons'
import { isKeySelection, isMultiSelection, isValueSelection } from 'svelte-jsoneditor'
import { compileJSONPointer, getIn } from 'immutable-json-patch'
import { getFocusPath, singleItemSelected } from '$lib/logic/selection'
import { isObjectOrArray } from '$lib/utils/typeUtils'
import { getEnforceString } from '$lib/logic/documentState'

<script lang="ts">
import {
faClone,
faCopy,
faCut,
faPaste,
faPen,
faPlus,
faTrashCan
} from '@fortawesome/free-solid-svg-icons'
import { compileJSONPointer, getIn } from 'immutable-json-patch'
import {
getFocusPath,
isKeySelection,
isMultiSelection,
isValueSelection,
singleItemSelected
} 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,
OnRenderContextMenuInternal
} from '$lib/types'
import { getEnforceString } from '$lib/logic/documentState.js'
import ContextMenu from '../../../../components/controls/contextmenu/ContextMenu.svelte'
export default function ({
json,
documentState,
parser,
onEditValue,
onEditRow,
onToggleEnforceString,
onCut,
onCopy,
onPaste,
onRemove,
onDuplicateRow,
onInsertBeforeRow,
onInsertAfterRow,
onRemoveRow
}: {
json: unknown | undefined
documentState: DocumentState
parser: JSONParser
onEditValue: () => void
onEditRow: () => void
onToggleEnforceString: () => void
onCut: (indent: boolean) => void
onCopy: (indent: boolean) => void
onPaste: () => void
onRemove: () => void
onDuplicateRow: () => void
onInsertBeforeRow: () => void
onInsertAfterRow: () => void
onRemoveRow: () => void
}): ContextMenuItem[] {
const selection = documentState.selection

export let json: unknown | undefined
export let documentState: DocumentState
export let parser: JSONParser
const hasJson = json !== undefined
const hasSelection = !!selection
const focusValue =
json !== undefined && selection ? getIn(json, getFocusPath(selection)) : undefined

export let showTip: boolean

export let onCloseContextMenu: () => void
export let onRenderContextMenu: OnRenderContextMenuInternal
export let onEditValue: () => void
export let onEditRow: () => void
export let onToggleEnforceString: () => void
export let onCut: (indent: boolean) => void
export let onCopy: (indent: boolean) => void
export let onPaste: () => void
export let onRemove: () => void
export let onDuplicateRow: () => void
export let onInsertBeforeRow: () => void
export let onInsertAfterRow: () => void
export let onRemoveRow: () => void

$: selection = documentState.selection

$: hasJson = json !== undefined
$: hasSelection = !!selection
$: focusValue = json !== undefined && selection ? getIn(json, getFocusPath(selection)) : undefined

$: hasSelectionContents =
const hasSelectionContents =
hasJson &&
(isMultiSelection(selection) || isKeySelection(selection) || isValueSelection(selection))

$: canEditValue = hasJson && selection != null && singleItemSelected(selection)
$: canEnforceString = canEditValue && !isObjectOrArray(focusValue)
const canEditValue = hasJson && selection != null && singleItemSelected(selection)
const canEnforceString = canEditValue && !isObjectOrArray(focusValue)

$: enforceString =
const enforceString =
selection != null && focusValue !== undefined
? getEnforceString(
focusValue,
Expand All @@ -72,8 +71,7 @@
)
: false

let defaultItems: ContextMenuItem[]
$: defaultItems = [
return [
{ type: 'separator' },
{
type: 'row',
Expand Down Expand Up @@ -239,12 +237,4 @@
]
}
]

$: items = onRenderContextMenu(defaultItems)
</script>

<ContextMenu
{items}
{onCloseContextMenu}
tip={showTip ? 'Tip: you can open this context menu via right-click or with Ctrl+Q' : undefined}
/>
}
Loading
Loading