From 8e43c03a1fc5138254aefb45de846324bfb9003d Mon Sep 17 00:00:00 2001 From: _Kerman Date: Fri, 12 Apr 2024 01:03:14 +0800 Subject: [PATCH] feat: context menu (#1475) Co-authored-by: Anthony Fu --- docs/.vitepress/config.ts | 4 + docs/custom/config-context-menu.md | 33 +++++ docs/custom/index.md | 2 + .../client/composables/useDragElements.ts | 4 +- packages/client/composables/useNav.ts | 20 ++++ packages/client/constants.ts | 1 + packages/client/env.ts | 2 + packages/client/internals/ContextMenu.vue | 110 +++++++++++++++++ packages/client/internals/Controls.vue | 2 + packages/client/internals/NavControls.vue | 17 +-- packages/client/logic/contextMenu.ts | 34 ++++++ packages/client/pages/play.vue | 6 +- packages/client/pages/presenter.vue | 4 + packages/client/setup/context-menu.ts | 113 ++++++++++++++++++ packages/parser/src/config.ts | 1 + packages/slidev/node/utils.ts | 46 ++++--- packages/slidev/node/virtual/setups.ts | 2 +- packages/slidev/node/vite/loaders.ts | 8 +- packages/types/client.d.ts | 7 ++ packages/types/src/config.ts | 6 + packages/types/src/context-menu.ts | 19 +++ packages/types/src/index.ts | 1 + packages/types/src/setups.ts | 47 +++----- packages/types/src/types.ts | 6 +- test/utils.test.ts | 22 +++- 25 files changed, 442 insertions(+), 75 deletions(-) create mode 100644 docs/custom/config-context-menu.md create mode 100644 packages/client/internals/ContextMenu.vue create mode 100644 packages/client/logic/contextMenu.ts create mode 100644 packages/client/setup/context-menu.ts create mode 100644 packages/types/src/context-menu.ts diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 0712cfd2b5..81dc564d11 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -156,6 +156,10 @@ const Customizations: (DefaultTheme.NavItemWithLink | DefaultTheme.NavItemChildr text: 'Configure Code Runners', link: '/custom/config-code-runners', }, + { + text: 'Configure Context Menu', + link: '/custom/config-context-menu', + }, { text: 'Vue Global Context', link: '/custom/vue-context', diff --git a/docs/custom/config-context-menu.md b/docs/custom/config-context-menu.md new file mode 100644 index 0000000000..12e6e1b152 --- /dev/null +++ b/docs/custom/config-context-menu.md @@ -0,0 +1,33 @@ +# Configure Context Menu + + + +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. diff --git a/docs/custom/index.md b/docs/custom/index.md index 7cf2af9329..54e8725b8e 100644 --- a/docs/custom/index.md +++ b/docs/custom/index.md @@ -55,6 +55,8 @@ remoteAssets: false selectable: true # enable slide recording, can be boolean, 'dev' or 'build' record: dev +# enable Slidev's context menu, can be boolean, 'dev' or 'build' +contextMenu: true # force color schema for the slides, can be 'auto', 'light', or 'dark' colorSchema: auto diff --git a/packages/client/composables/useDragElements.ts b/packages/client/composables/useDragElements.ts index 884c2a0bb6..242925d972 100644 --- a/packages/client/composables/useDragElements.ts +++ b/packages/client/composables/useDragElements.ts @@ -52,7 +52,9 @@ export function useDragElementsUpdater(no: number) { return frontmatter.dragPos[id] = posStr newPatch = { - dragPos: frontmatter.dragPos, + frontmatter: { + dragPos: frontmatter.dragPos, + }, } } else { diff --git a/packages/client/composables/useNav.ts b/packages/client/composables/useNav.ts index 8653bb6b20..0460069c65 100644 --- a/packages/client/composables/useNav.ts +++ b/packages/client/composables/useNav.ts @@ -60,6 +60,11 @@ export interface SlidevContextNav { goFirst: () => Promise /** Go to the last slide */ goLast: () => Promise + + /** Enter presenter mode */ + enterPresenter: () => void + /** Exit presenter mode */ + exitPresenter: () => void } export interface SlidevContextNavState { @@ -194,6 +199,19 @@ export function useNavBase( } } + function enterPresenter() { + router?.push({ + path: getSlidePath(currentSlideNo.value, true), + query: { ...router.currentRoute.value.query }, + }) + } + function exitPresenter() { + router?.push({ + path: getSlidePath(currentSlideNo.value, false), + query: { ...router.currentRoute.value.query }, + }) + } + return { slides, total, @@ -222,6 +240,8 @@ export function useNavBase( goFirst, nextSlide, prevSlide, + enterPresenter, + exitPresenter, } } diff --git a/packages/client/constants.ts b/packages/client/constants.ts index faee5a2466..8f5caa8c90 100644 --- a/packages/client/constants.ts +++ b/packages/client/constants.ts @@ -76,4 +76,5 @@ export const HEADMATTER_FIELDS = [ 'drawings', 'htmlAttrs', 'mdc', + 'contextMenu', ] diff --git a/packages/client/env.ts b/packages/client/env.ts index a8cee09168..fc9de7674b 100644 --- a/packages/client/env.ts +++ b/packages/client/env.ts @@ -4,6 +4,8 @@ import configs from '#slidev/configs' export { configs } +export const mode = __DEV__ ? 'dev' : 'build' + export const slideAspect = ref(configs.aspectRatio ?? (16 / 9)) export const slideWidth = ref(configs.canvasWidth ?? 980) diff --git a/packages/client/internals/ContextMenu.vue b/packages/client/internals/ContextMenu.vue new file mode 100644 index 0000000000..2057a17d0c --- /dev/null +++ b/packages/client/internals/ContextMenu.vue @@ -0,0 +1,110 @@ + + + diff --git a/packages/client/internals/Controls.vue b/packages/client/internals/Controls.vue index dc8d83c28b..8c0e716ece 100644 --- a/packages/client/internals/Controls.vue +++ b/packages/client/internals/Controls.vue @@ -5,6 +5,7 @@ import { configs } from '../env' import QuickOverview from './QuickOverview.vue' import InfoDialog from './InfoDialog.vue' import Goto from './Goto.vue' +import ContextMenu from './ContextMenu.vue' const WebCamera = shallowRef() const RecordingDialog = shallowRef() @@ -20,4 +21,5 @@ if (__SLIDEV_FEATURE_RECORD__) { + diff --git a/packages/client/internals/NavControls.vue b/packages/client/internals/NavControls.vue index 242a401483..d531eb6651 100644 --- a/packages/client/internals/NavControls.vue +++ b/packages/client/internals/NavControls.vue @@ -5,7 +5,6 @@ import { downloadPDF } from '../utils' import { activeElement, breakpoints, fullscreen, presenterLayout, showEditor, showInfoDialog, showPresenterCursor, toggleOverview, togglePresenterLayout } from '../state' import { configs } from '../env' import { useNav } from '../composables/useNav' -import { getSlidePath } from '../logic/slides' import { useDrawings } from '../composables/useDrawings' import Settings from './Settings.vue' import MenuButton from './MenuButton.vue' @@ -21,7 +20,6 @@ const props = defineProps({ }) const { - currentRoute, currentSlideNo, hasNext, hasPrev, @@ -31,6 +29,8 @@ const { next, prev, total, + enterPresenter, + exitPresenter, } = useNav() const { brush, @@ -40,11 +40,6 @@ const { const md = breakpoints.smaller('md') const { isFullscreen, toggle: toggleFullscreen } = fullscreen -const presenterPassword = computed(() => currentRoute.value.query.password) -const query = computed(() => presenterPassword.value ? `?password=${presenterPassword.value}` : '') -const presenterLink = computed(() => `${getSlidePath(currentSlideNo.value, true)}${query.value}`) -const nonPresenterLink = computed(() => `${getSlidePath(currentSlideNo.value, false)}${query.value}`) - const root = ref() function onMouseLeave() { if (root.value && activeElement.value && root.value.contains(activeElement.value)) @@ -124,12 +119,12 @@ if (__SLIDEV_FEATURE_DRAWINGS__)