Nuxt UI v3-alpha 已发布!

试用
组件

上下文菜单

显示一个在右键点击时出现的菜单。

使用

右键点击这里
<script setup lang="ts">
import { useMouse, useWindowScroll } from '@vueuse/core'

const { x, y } = useMouse()
const { y: windowY } = useWindowScroll()

const isOpen = ref(false)
const virtualElement = ref({ getBoundingClientRect: () => ({}) })

function onContextMenu() {
  const top = unref(y) - unref(windowY)
  const left = unref(x)

  virtualElement.value.getBoundingClientRect = () => ({
    width: 0,
    height: 0,
    top,
    left
  })

  isOpen.value = true
}
</script>

<template>
  <div class="w-full" @contextmenu.prevent="onContextMenu">
    <Placeholder class="h-96 select-none w-full flex items-center justify-center">
      Right click here
    </Placeholder>

    <UContextMenu v-model="isOpen" :virtual-element="virtualElement">
      <div class="p-4">
        Menu
      </div>
    </UContextMenu>
  </div>
</template>

Popper

使用 popper 属性自定义 Popper 实例。

箭头

右键点击这里
<script setup lang="ts">
import { useMouse, useWindowScroll } from '@vueuse/core'

const { x, y } = useMouse()
const { y: windowY } = useWindowScroll()

const isOpen = ref(false)
const virtualElement = ref({ getBoundingClientRect: () => ({}) })

function onContextMenu() {
  const top = unref(y) - unref(windowY)
  const left = unref(x)

  virtualElement.value.getBoundingClientRect = () => ({
    width: 0,
    height: 0,
    top,
    left
  })

  isOpen.value = true
}
</script>

<template>
  <div class="w-full" @contextmenu.prevent="onContextMenu">
    <Placeholder class="h-96 select-none w-full flex items-center justify-center">
      Right click here
    </Placeholder>

    <UContextMenu v-model="isOpen" :virtual-element="virtualElement" :popper="{ arrow: true, placement: 'right' }">
      <div class="p-4">
        Menu
      </div>
    </UContextMenu>
  </div>
</template>

位置

右键点击这里
<script setup lang="ts">
import { useMouse, useWindowScroll } from '@vueuse/core'

const { x, y } = useMouse()
const { y: windowY } = useWindowScroll()

const isOpen = ref(false)
const virtualElement = ref({ getBoundingClientRect: () => ({}) })

function onContextMenu() {
  const top = unref(y) - unref(windowY)
  const left = unref(x)

  virtualElement.value.getBoundingClientRect = () => ({
    width: 0,
    height: 0,
    top,
    left
  })

  isOpen.value = true
}
</script>

<template>
  <div class="w-full" @contextmenu.prevent="onContextMenu">
    <Placeholder class="h-96 select-none w-full flex items-center justify-center">
      Right click here
    </Placeholder>

    <UContextMenu v-model="isOpen" :virtual-element="virtualElement" :popper="{ placement: 'right-start' }">
      <div class="p-4">
        Menu
      </div>
    </UContextMenu>
  </div>
</template>

偏移

右键点击这里
<script setup lang="ts">
import { useMouse, useWindowScroll } from '@vueuse/core'

const { x, y } = useMouse()
const { y: windowY } = useWindowScroll()

const isOpen = ref(false)
const virtualElement = ref({ getBoundingClientRect: () => ({}) })

function onContextMenu() {
  const top = unref(y) - unref(windowY)
  const left = unref(x)

  virtualElement.value.getBoundingClientRect = () => ({
    width: 0,
    height: 0,
    top,
    left
  })

  isOpen.value = true
}
</script>

<template>
  <div class="w-full" @contextmenu.prevent="onContextMenu">
    <Placeholder class="h-96 select-none w-full flex items-center justify-center">
      Right click here
    </Placeholder>

    <UContextMenu v-model="isOpen" :virtual-element="virtualElement" :popper="{ offset: 0 }">
      <div class="p-4">
        Menu
      </div>
    </UContextMenu>
  </div>
</template>

属性

virtualElement必需
Record<string, any>
ui
{ wrapper?: string; container?: string; width?: string; background?: string; shadow?: string; rounded?: string; ring?: string; base?: string; transition?: DeepPartial<{ enterActiveClass: string; enterFromClass: string; enterToClass: string; leaveActiveClass: string; leaveFromClass: string; leaveToClass: string; }, a...
{}
popper
PopperOptions
{}
modelValue
boolean
false

配置

{
  wrapper: 'relative',
  container: 'z-20 group',
  width: '',
  background: 'bg-white dark:bg-gray-900',
  shadow: 'shadow-lg',
  rounded: 'rounded-md',
  ring: 'ring-1 ring-gray-200 dark:ring-gray-800',
  base: 'overflow-hidden focus:outline-none relative',
  transition: {
    enterActiveClass: 'transition ease-out duration-200',
    enterFromClass: 'opacity-0 translate-y-1',
    enterToClass: 'opacity-100 translate-y-0',
    leaveActiveClass: 'transition ease-in duration-150',
    leaveFromClass: 'opacity-100 translate-y-0',
    leaveToClass: 'opacity-0 translate-y-1'
  },
  popper: {
    placement: 'bottom-start',
    scroll: false
  },
  arrow: {
    base: 'invisible before:visible before:block before:rotate-45 before:z-[-1] before:w-2 before:h-2',
    ring: 'before:ring-1 before:ring-gray-200 dark:before:ring-gray-800',
    rounded: 'before:rounded-sm',
    background: 'before:bg-gray-200 dark:before:bg-gray-800',
    shadow: 'before:shadow',
    placement: "group-data-[popper-placement*='right']:-left-1 group-data-[popper-placement*='left']:-right-1 group-data-[popper-placement*='top']:-bottom-1 group-data-[popper-placement*='bottom']:-top-1"
  }
}