zs
2025-05-06 5f1a99026511f4ed521891829b67ef6c177f82e5
HIAWms/web/src/components/vue3-context-menu/MenuBar.vue
对比新文件
@@ -0,0 +1,195 @@
<template>
  <div
    :class="[
      'mx-menu-bar',
      options.theme ?? '',
      options.mini ? 'mini' : '',
    ]"
    @focus="onFocus"
    @blur="onBlur"
  >
    <slot name="prefix" />
    <div
      v-if="options.mini"
      ref="menuBarContent"
      class="mx-menu-bar-content"
    >
      <div
        class="mx-menu-bar-item"
        @click="onItemClick(0, null)"
      >
        <MenuBarIconMenu />
      </div>
    </div>
    <div
      v-else
      ref="menuBarContent"
      class="mx-menu-bar-content"
    >
      <div
        v-for="(item, key) in menuItems"
        :key="key"
        :class="[
          'mx-menu-bar-item',
          item == menuActive ? 'active' : '',
        ]"
        @click="onItemClick(key, item as MenuItem)"
        @mouseenter="onItemEnter(key, item as MenuItem)"
      >
        {{ item.label }}
      </div>
    </div>
    <slot name="suffix" />
  </div>
</template>
<script setup lang="ts">
/**
 * Menu bar component
 */
import { ref, type PropType, onMounted, watch } from 'vue';
import type { MenuBarOptions } from './MenuBar';
import type { ContextMenuInstance, MenuItem } from './ContextMenuDefine';
import { getTop, getLeft } from './ContextMenuUtils';
import ContextMenu from './ContextMenuInstance';
import MenuBarIconMenu from './MenuBarIconMenu.vue';
const props = defineProps({
  /**
   * Menu options
   */
  options: {
    type: Object as PropType<MenuBarOptions>,
    default: null
  }
});
const menuBarContent = ref<HTMLDivElement>();
const menuBarActive = ref(false);
const menuItems = ref<MenuItem[]>([]);
const menuActive = ref<MenuItem|null>(null);
function onFocus() {
  menuBarActive.value = true;
}
function onBlur() {
  menuBarActive.value = false;
}
onMounted(() => {
  (menuItems.value as MenuItem[]) = props.options.items || [];
});
watch(() => props.options, () => {
  (menuItems.value as MenuItem[]) = props.options.items || [];
});
let currentMenu : ContextMenuInstance|null = null;
let currentMenuIndex = -1;
function showNextSubMenu() {
  if (currentMenuIndex < menuItems.value.length - 1)
    currentMenuIndex++;
  else
    currentMenuIndex = 0;
  showSubMenu(currentMenuIndex, menuItems.value[currentMenuIndex] as MenuItem);
}
function showPrevSubMenu() {
  if (currentMenuIndex > 0)
    currentMenuIndex--;
  else
    currentMenuIndex = menuItems.value.length - 1;
  showSubMenu(currentMenuIndex, menuItems.value[currentMenuIndex] as MenuItem);
}
function getMenuShowPos(ele: HTMLElement) {
  const showDirection = props.options.barPopDirection ?? 'bl';
  let x = 0;
  let y = 0;
  if (showDirection.startsWith('b'))
    y = getTop(ele) + ele.offsetHeight;
  else if (showDirection.startsWith('t'))
    y = getTop(ele);
  else
    y = getTop(ele) + ele.offsetHeight / 2;
  if (showDirection.endsWith('l'))
    x = getLeft(ele);
  else if (showDirection.startsWith('r'))
    x = getLeft(ele) + ele.offsetWidth;
  else
    x = getLeft(ele) + ele.offsetWidth / 2;
  return { x, y }
}
function showSubMenu(index: number, item: MenuItem) {
  currentMenuIndex = index;
  if (!item.children)
    return;
  if (currentMenu) {
    currentMenu.closeMenu();
    currentMenu = null;
    menuBarActive.value = true;
  }
  (menuActive.value as MenuItem) = item;
  const ele = menuBarContent.value?.children[index] as HTMLElement;
  if (ele) {
    const { x, y } = getMenuShowPos(ele);
    currentMenu = ContextMenu.showContextMenu({
      ...props.options,
      items: item.children,
      x,
      y,
      onKeyFocusMoveLeft() {
        showPrevSubMenu();
      },
      onKeyFocusMoveRight() {
        showNextSubMenu();
      },
      onClose() {
        if (menuActive.value == item) {
          menuBarActive.value = false;
          menuActive.value = null;
        }
      },
    });
  }
}
function showAllSubMenu() {
  currentMenuIndex = 0;
  const ele = menuBarContent.value as HTMLElement;
  if (ele) {
    const { x, y } = getMenuShowPos(ele);
    currentMenu = ContextMenu.showContextMenu({
      ...props.options,
      x,
      y,
    });
  }
}
function onItemClick(index: number, item: MenuItem|null) {
  if (item) {
    menuBarActive.value = true;
    showSubMenu(index, item);
    if (
      item.onClick && (
      (item.clickableWhenHasChildren === true && item.children && item.children.length > 0)
      || !item.children || item.children.length === 0)
    )
      item.onClick();
  } else {
    showAllSubMenu();
  }
}
function onItemEnter(index: number, item: MenuItem) {
  if (menuBarActive.value) {
    showSubMenu(index, item);
  }
}
</script>
<style lang="scss">
@import './MenuBar.scss';
</style>