CommonDropdown.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import { computed } from 'vue'
  4. import CommonPopover from '#shared/components/CommonPopover/CommonPopover.vue'
  5. import { usePopover } from '#shared/components/CommonPopover/usePopover.ts'
  6. import { EnumTextDirection } from '#shared/graphql/types.ts'
  7. import { i18n } from '#shared/i18n/index.ts'
  8. import { useLocaleStore } from '#shared/stores/locale.ts'
  9. import CommonButton from '#desktop/components/CommonButton/CommonButton.vue'
  10. import type { DropdownItem } from '#desktop/components/CommonDropdown/types.ts'
  11. import CommonPopoverMenu from '#desktop/components/CommonPopoverMenu/CommonPopoverMenu.vue'
  12. import CommonPopoverMenuItem from '#desktop/components/CommonPopoverMenu/CommonPopoverMenuItem.vue'
  13. import type { MenuItem } from '#desktop/components/CommonPopoverMenu/types.ts'
  14. interface Props {
  15. items: MenuItem[]
  16. orientation?: 'top' | 'bottom'
  17. /**
  18. * Will apply on the button label if v-model is not bound
  19. * */
  20. actionLabel?: string
  21. }
  22. const props = withDefaults(defineProps<Props>(), {
  23. orientation: 'bottom',
  24. })
  25. const emit = defineEmits<{
  26. 'handle-action': [DropdownItem]
  27. }>()
  28. const { popover, popoverTarget, isOpen, toggle } = usePopover()
  29. const locale = useLocaleStore()
  30. const currentPopoverPlacement = computed(() => {
  31. if (locale.localeData?.dir === EnumTextDirection.Rtl) return 'start'
  32. return 'end'
  33. })
  34. /**
  35. * MenuItem transformed into a radio button model
  36. * */
  37. const modelValue = defineModel<DropdownItem>()
  38. const dropdownLabel = computed(() =>
  39. modelValue.value ? i18n.t(modelValue.value?.label) : props.actionLabel,
  40. )
  41. const handleSelectRadio = (item: DropdownItem) => {
  42. modelValue.value = item
  43. toggle()
  44. }
  45. const actionItems = computed(() =>
  46. props.items.map((item) => ({
  47. ...item,
  48. onClick: () => emit('handle-action', item),
  49. })),
  50. )
  51. </script>
  52. <template>
  53. <CommonPopover
  54. ref="popover"
  55. :owner="popoverTarget"
  56. :placement="currentPopoverPlacement"
  57. :orientation="orientation"
  58. >
  59. <CommonPopoverMenu v-if="modelValue" :popover="popover" :items="items">
  60. <template v-for="item in items" :key="item.key" #[`item-${item.key}`]>
  61. <div class="group flex grow cursor-pointer items-center">
  62. <CommonPopoverMenuItem
  63. class="flex grow items-center gap-2 p-2.5"
  64. :label="item.label"
  65. :variant="item.variant"
  66. :link="item.link"
  67. :icon="item.icon"
  68. :label-placeholder="item.labelPlaceholder"
  69. role="checkbox"
  70. :aria-checked="modelValue.key === item.key"
  71. @click="handleSelectRadio(item)"
  72. >
  73. <template #leading>
  74. <CommonIcon
  75. :class="{ 'opacity-0': modelValue.key !== item.key }"
  76. size="tiny"
  77. name="check2"
  78. />
  79. </template>
  80. </CommonPopoverMenuItem>
  81. </div>
  82. </template>
  83. </CommonPopoverMenu>
  84. <CommonPopoverMenu v-else :popover="popover" :items="actionItems" />
  85. </CommonPopover>
  86. <CommonButton
  87. v-bind="$attrs"
  88. ref="popoverTarget"
  89. class="group"
  90. :class="{
  91. 'hover:bg-blue-600 hover:text-black dark:hover:bg-blue-900 dark:hover:text-white':
  92. !isOpen,
  93. 'bg-blue-800 text-white hover:bg-blue-800': isOpen,
  94. }"
  95. size="large"
  96. variant="secondary"
  97. @click="toggle"
  98. >
  99. <template #label>
  100. <span class="truncate">
  101. {{ dropdownLabel }}
  102. </span>
  103. <CommonIcon
  104. size="small"
  105. decorative
  106. class="pointer-events-none shrink-0 text-stone-200 transition duration-200 dark:text-neutral-500 dark:group-hover:text-white"
  107. :class="{
  108. 'text-white dark:text-white': isOpen,
  109. 'group-hover:text-black dark:group-hover:text-white': !isOpen,
  110. }"
  111. name="chevron-up"
  112. />
  113. </template>
  114. </CommonButton>
  115. </template>