-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
- Loading branch information
Showing
25 changed files
with
442 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# Configure Context Menu | ||
|
||
<Environment type="client" /> | ||
|
||
Customize the context menu items in Slidev. | ||
|
||
Create `./setup/context-menu.ts` with the following content: | ||
|
||
```ts | ||
import { defineContextMenuSetup } from '@slidev/types' | ||
import { computed } from 'vue' | ||
import Icon3DCursor from '~icons/carbon/3d-cursor' | ||
|
||
export default defineContextMenuSetup((items) => { | ||
const { isPresenter } = useNav() | ||
return computed(() => [ | ||
...items.value, | ||
{ | ||
small: false, | ||
icon: Icon3DCursor, // Used as `title` if `small` is `true` | ||
label: 'Custom Menu Item', // or a Vue component | ||
action() { | ||
alert('Custom Menu Item Clicked!') | ||
}, | ||
disabled: isPresenter.value, | ||
}, | ||
]) | ||
}) | ||
``` | ||
|
||
This will append a new menu item to the context menu. | ||
|
||
To disable context menu globally, set `contextMenu` to `false` in the frontmatter. `contextMenu` can also be set to `dev` or `build` to only enable the context menu in development or build mode. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,4 +76,5 @@ export const HEADMATTER_FIELDS = [ | |
'drawings', | ||
'htmlAttrs', | ||
'mdc', | ||
'contextMenu', | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
<script setup lang="ts"> | ||
import { onClickOutside, useElementBounding, useEventListener, useWindowFocus } from '@vueuse/core' | ||
import { computed, ref, watch } from 'vue' | ||
import { closeContextMenu, currentContextMenu } from '../logic/contextMenu' | ||
import { useDynamicSlideInfo } from '../composables/useSlideInfo' | ||
import { windowSize } from '../state' | ||
import { configs } from '../env' | ||
const container = ref<HTMLElement>() | ||
onClickOutside(container, closeContextMenu) | ||
useEventListener(document, 'mousedown', (ev) => { | ||
if (ev.buttons & 2) | ||
closeContextMenu() | ||
}, { | ||
passive: true, | ||
capture: true, | ||
}) | ||
const isExplicitEnabled = computed(() => configs.contextMenu != null) | ||
const windowFocus = useWindowFocus() | ||
watch(windowFocus, (hasFocus) => { | ||
if (!hasFocus) | ||
closeContextMenu() | ||
}) | ||
const firstSlide = useDynamicSlideInfo(1) | ||
function disableContextMenu() { | ||
const info = firstSlide.info.value | ||
if (!info) | ||
return | ||
firstSlide.update({ | ||
frontmatter: { | ||
contextMenu: false, | ||
}, | ||
}) | ||
} | ||
const { width, height } = useElementBounding(container) | ||
const left = computed(() => { | ||
const x = currentContextMenu.value?.x | ||
if (!x) | ||
return 0 | ||
if (x + width.value > windowSize.width.value) | ||
return windowSize.width.value - width.value | ||
return x | ||
}) | ||
const top = computed(() => { | ||
const y = currentContextMenu.value?.y | ||
if (!y) | ||
return 0 | ||
if (y + height.value > windowSize.height.value) | ||
return windowSize.height.value - height.value | ||
return y | ||
}) | ||
</script> | ||
|
||
<template> | ||
<div | ||
v-if="currentContextMenu" | ||
ref="container" | ||
:style="`left:${left}px;top:${top}px`" | ||
class="fixed z-100 w-60 flex flex-wrap justify-items-start p-1 animate-fade-in animate-duration-100 backdrop-blur bg-main bg-opacity-75! border border-main rounded-md shadow overflow-hidden select-none" | ||
@contextmenu.prevent="" | ||
@click="closeContextMenu" | ||
> | ||
<template v-for="item, index of currentContextMenu.items.value" :key="index"> | ||
<div v-if="item === 'separator'" :key="index" class="w-full my1 border-t border-main" /> | ||
<div | ||
v-else-if="item.small" | ||
class="p-2 w-[40px] h-[40px] inline-block text-center cursor-pointer rounded" | ||
:class="item.disabled ? `op40` : `hover:bg-active`" | ||
:title="(item.label as string)" | ||
@click="item.action" | ||
> | ||
<component :is="item.icon" /> | ||
</div> | ||
<div | ||
v-else | ||
class="w-full grid grid-cols-[35px_1fr] p-2 pl-0 cursor-pointer rounded" | ||
:class="item.disabled ? `op40` : `hover:bg-active`" | ||
@click="item.action" | ||
> | ||
<div class="mx-auto"> | ||
<component :is="item.icon" /> | ||
</div> | ||
<div v-if="typeof item.label === 'string'"> | ||
{{ item.label }} | ||
</div> | ||
<component :is="item.label" v-else /> | ||
</div> | ||
</template> | ||
<template v-if="!isExplicitEnabled"> | ||
<div class="w-full my1 border-t border-main" /> | ||
<div class="w-full text-xs p2"> | ||
<div class="text-main text-opacity-50!"> | ||
Hold <kbd class="border px1 py0.5 border-main rounded text-primary">Shift</kbd> and right click to open the native context menu | ||
<button | ||
v-if="__DEV__" | ||
class="underline op50 hover:op100 mt1 block" | ||
@click="disableContextMenu()" | ||
> | ||
Disable custom context menu | ||
</button> | ||
</div> | ||
</div> | ||
</template> | ||
</div> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import type { ContextMenuItem } from '@slidev/types' | ||
import type { ComputedRef } from 'vue' | ||
import { shallowRef } from 'vue' | ||
import setupContextMenu from '../setup/context-menu' | ||
import { configs, mode } from '../env' | ||
|
||
export const currentContextMenu = shallowRef<null | { | ||
x: number | ||
y: number | ||
items: ComputedRef<ContextMenuItem[]> | ||
}>(null) | ||
|
||
export function openContextMenu(x: number, y: number) { | ||
currentContextMenu.value = { | ||
x, | ||
y, | ||
items: setupContextMenu(), | ||
} | ||
} | ||
|
||
export function closeContextMenu() { | ||
currentContextMenu.value = null | ||
} | ||
|
||
export function onContextMenu(ev: MouseEvent) { | ||
if (configs.contextMenu !== true && configs.contextMenu !== undefined && configs.contextMenu !== mode) | ||
return | ||
if (ev.shiftKey || ev.defaultPrevented) | ||
return | ||
|
||
openContextMenu(ev.pageX, ev.pageY) | ||
ev.preventDefault() | ||
ev.stopPropagation() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.