Skip to content
Snippets Groups Projects
MenuDial.vue 4.1 KiB
Newer Older
<script setup lang="ts">
import { useVModel } from '@vueuse/core';
import PlusIcon from '@/shared/icons/PlusIcon.vue';
import { computed } from 'vue';
import { convertThemeToColorWhiteDefault } from '@/app/helpers';

interface Props {
  isActive: boolean;
  items: {
    label: string;
    theme?: string;
    textStyle?: 'bold' | 'italic';
    onClick?: () => void;
  }[];
  size?: 'large' | 'extraLarge';
  theme?: string;
  direction?: 'left' | 'right' | 'up' | 'down';
}
const props = defineProps<Props>();
const emit = defineEmits(['update:isActive']);
const isActive = useVModel(props, 'isActive', emit);

const themeColor = computed(() => convertThemeToColorWhiteDefault(props.theme));
const textColor = computed(() => {
  if (!props.theme) return '#000000';
  if (props.theme === 'white') return '#000000';
  return '#ffffff';
});
const elementsSize = computed(() => {
  if (!props?.size) return 30;
  switch (props.size) {
    case 'large':
      return 40;
    case 'extraLarge':
      return 50;
  }
});
const menuListStyles = computed(() => {
  if (!props.direction)
    return `transform: translateY(-${elementsSize.value / 2}px) translateX(${isActive.value ? elementsSize.value + 10 : 0}px)`;
  switch (props.direction) {
    case 'right':
      return `transform: translateY(-${elementsSize.value / 2}px) translateX(${isActive.value ? elementsSize.value + 10 : 0}px)`;
    case 'left':
      return `flex-direction: row-reverse; transform: translateY(-${elementsSize.value / 2}px) ${isActive.value ? `translateX(calc(-100% - 10px))` : ''}`;
    case 'up':
      return `transform: translateY(-${isActive.value ? (0.5 + props.items.length) * elementsSize.value + 10 : elementsSize.value / 2}px)`;
    case 'down':
      return `transform: translateY(${isActive.value ? 20 : 0}px)`;
  }
});
</script>

<template>
  <section class="menuContainer">
    <button
      :style="`background-color: ${themeColor ?? 'white'}; width: ${elementsSize}px; height: ${elementsSize}px; transform: ${isActive ? 'rotate(135deg)' : ''};`"
      class="menuButton"
      @click.prevent="isActive = !isActive"
    >
      <slot name="buttonIcon" />
      <PlusIcon v-if="!$slots.buttonIcon" :size="elementsSize - 10" :color="textColor" />
    </button>
    <ul
      :class="[
        'menuList',
        {
          menuListColumn: direction === 'up' || direction === 'down',
          menuListOpened: isActive
        }
      ]"
      :style="menuListStyles"
    >
      <li
        v-for="(item, index) of items"
        :key="item.label"
        :style="`height: ${elementsSize}px; background-color: ${convertThemeToColorWhiteDefault(item.theme)};
        color: ${!item.theme || item.theme === 'white' ? 'black' : 'white'}; border-color: ${!item.theme || item.theme === 'white' ? 'black' : 'white'}`"
        class="menuElement"
        @click.prevent="item?.onClick"
      >
        <slot :name="`${index + 1}IconBefore`" />
        <p
          style="padding: 5px"
          :class="[
            '',
            {
              bold: item.textStyle === 'bold',
              italic: item.textStyle === 'italic'
            }
          ]"
        >
          {{ item.label }}
        </p>
        <slot :name="`${index + 1}IconAfter`" />
      </li>
    </ul>
  </section>
</template>

<style scoped>
.menuContainer {
  position: relative;
}
.menuButton {
  position: relative;
  border: 2px solid black;
  z-index: 2;
  display: flex;
  justify-content: center;
  align-items: center;
  border-radius: 50%;
  transition: 0.2s ease-in-out;
}
.menuButton:hover {
  filter: brightness(75%);
}
.menuList {
  display: flex;
  pointer-events: none;
  gap: 10px;
  position: absolute;
  top: 50%;
  left: 0;
  opacity: 0;
  transition: 0.2s ease-in-out;
}
.menuListColumn {
  flex-direction: column;
}
.menuListOpened {
  pointer-events: auto;
  opacity: 1;
}
.menuElement {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 10px;
  border: 1px solid white;
  border-radius: 5px;
  user-select: none;
  cursor: pointer;
  transition: 0.2s ease-in-out;
}
.menuElement:hover {
  filter: brightness(75%);
}
.bold {
  font-weight: bold;
}
.italic {
  font-style: italic;
}
</style>