When right-clicking a component, the context menu now shows 'Create Instance' instead of 'Create Component'. Previously both were shown simultaneously.
130 lines
4.4 KiB
TypeScript
130 lines
4.4 KiB
TypeScript
import { useStore } from '@nanostores/vue'
|
|
import { computed } from 'vue'
|
|
|
|
import { useEditorCommands } from '@open-pencil/vue/commands/useEditorCommands'
|
|
import { useEditor } from '@open-pencil/vue/context/editorContext'
|
|
import { menuMessages } from '@open-pencil/vue/i18n'
|
|
import { useSelectionState } from '@open-pencil/vue/selection/useSelectionState'
|
|
|
|
/**
|
|
* Action entry used by menu models returned from {@link useMenuModel}.
|
|
*/
|
|
export interface MenuActionNode {
|
|
separator?: false
|
|
label: string
|
|
shortcut?: string
|
|
action?: () => void
|
|
disabled?: boolean
|
|
checked?: boolean
|
|
onCheckedChange?: (checked: boolean) => void
|
|
sub?: MenuEntry[]
|
|
}
|
|
|
|
export interface MenuSeparatorNode {
|
|
separator: true
|
|
}
|
|
|
|
export type MenuEntry = MenuActionNode | MenuSeparatorNode
|
|
|
|
/**
|
|
* Returns ready-to-render menu models derived from the current editor state.
|
|
*
|
|
* This is a higher-level API than {@link useEditorCommands}: it groups
|
|
* commands into app and canvas menu structures and computes context-sensitive
|
|
* labels like Hide/Show and Lock/Unlock.
|
|
*/
|
|
export function useMenuModel() {
|
|
const editor = useEditor()
|
|
const { menuItem: commandMenuItem, otherPages, moveSelectionToPage } = useEditorCommands()
|
|
const { hasSelection, isGroup, isInstance, isComponent, canCreateComponentSet } =
|
|
useSelectionState()
|
|
|
|
const t = useStore(menuMessages)
|
|
|
|
const editMenu = computed<MenuEntry[]>(() => [
|
|
commandMenuItem('edit.undo'),
|
|
commandMenuItem('edit.redo'),
|
|
{ separator: true },
|
|
commandMenuItem('selection.duplicate'),
|
|
commandMenuItem('selection.delete'),
|
|
{ separator: true },
|
|
commandMenuItem('selection.selectAll')
|
|
])
|
|
|
|
const viewMenu = computed<MenuEntry[]>(() => [
|
|
commandMenuItem('view.zoom100'),
|
|
commandMenuItem('view.zoomFit'),
|
|
commandMenuItem('view.zoomSelection')
|
|
])
|
|
|
|
const objectMenu = computed<MenuEntry[]>(() => [
|
|
commandMenuItem('selection.group'),
|
|
commandMenuItem('selection.ungroup'),
|
|
{ separator: true },
|
|
commandMenuItem('selection.createComponent'),
|
|
commandMenuItem('selection.createComponentSet'),
|
|
commandMenuItem('selection.detachInstance'),
|
|
{ separator: true },
|
|
commandMenuItem('selection.bringToFront'),
|
|
commandMenuItem('selection.sendToBack')
|
|
])
|
|
|
|
const arrangeMenu = computed<MenuEntry[]>(() => [commandMenuItem('selection.wrapInAutoLayout')])
|
|
|
|
const appMenu = computed(() => [
|
|
{ label: t.value.edit, items: editMenu.value },
|
|
{ label: t.value.view, items: viewMenu.value },
|
|
{ label: t.value.object, items: objectMenu.value },
|
|
{ label: t.value.arrange, items: arrangeMenu.value }
|
|
])
|
|
|
|
const canvasMenu = computed<MenuEntry[]>(() => {
|
|
const moveToPageSubmenu: MenuEntry[] = otherPages.value.map((page) => ({
|
|
label: page.name,
|
|
action: () => moveSelectionToPage(page.id)
|
|
}))
|
|
|
|
return [
|
|
commandMenuItem('selection.duplicate', '⌘D'),
|
|
commandMenuItem('selection.delete', '⌫'),
|
|
{ separator: true },
|
|
...(moveToPageSubmenu.length > 0 && hasSelection.value
|
|
? [{ label: t.value.moveToPage, sub: moveToPageSubmenu } satisfies MenuActionNode]
|
|
: []),
|
|
commandMenuItem('selection.bringToFront', ']'),
|
|
commandMenuItem('selection.sendToBack', '['),
|
|
{ separator: true },
|
|
commandMenuItem('selection.group', '⌘G'),
|
|
...(isGroup.value ? [commandMenuItem('selection.ungroup', '⇧⌘G')] : []),
|
|
...(hasSelection.value ? [commandMenuItem('selection.wrapInAutoLayout', '⇧A')] : []),
|
|
{ separator: true },
|
|
...(isComponent.value
|
|
? [commandMenuItem('selection.createInstance')]
|
|
: [commandMenuItem('selection.createComponent', '⌥⌘K')]),
|
|
...(canCreateComponentSet.value
|
|
? [commandMenuItem('selection.createComponentSet', '⇧⌘K')]
|
|
: []),
|
|
...(isInstance.value ? [commandMenuItem('selection.goToMainComponent')] : []),
|
|
...(isInstance.value ? [commandMenuItem('selection.detachInstance', '⌥⌘B')] : []),
|
|
...(hasSelection.value
|
|
? [
|
|
{ separator: true } as MenuSeparatorNode,
|
|
commandMenuItem('selection.toggleVisibility', '⇧⌘H'),
|
|
commandMenuItem('selection.toggleLock', '⇧⌘L')
|
|
]
|
|
: [])
|
|
]
|
|
})
|
|
|
|
const selectionLabelMenu = computed(() => ({
|
|
visibility: (editor.getSelectedNode()?.visible ?? true) ? t.value.hide : t.value.show,
|
|
lock: (editor.getSelectedNode()?.locked ?? false) ? t.value.unlock : t.value.lock
|
|
}))
|
|
|
|
return {
|
|
appMenu,
|
|
canvasMenu,
|
|
selectionLabelMenu
|
|
}
|
|
}
|