LayoutHeader.vue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. <!-- Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import { computed, useTemplateRef, useSlots, type SetupContext } from 'vue'
  4. import CommonBackButton from '#mobile/components/CommonBackButton/CommonBackButton.vue'
  5. import CommonButton from '#mobile/components/CommonButton/CommonButton.vue'
  6. import type { CommonButtonProps } from '#mobile/components/CommonButton/types.ts'
  7. import CommonRefetch from '#mobile/components/CommonRefetch/CommonRefetch.vue'
  8. import type { RouteLocationRaw } from 'vue-router'
  9. export interface Props {
  10. containerTag?: 'header' | 'div'
  11. title?: string
  12. titleClass?: string
  13. backTitle?: string
  14. backIgnore?: string[]
  15. backUrl?: RouteLocationRaw
  16. backAvoidHomeButton?: boolean
  17. defaultAttrs?: Record<string, unknown>
  18. refetch?: boolean
  19. actionTitle?: string
  20. actionHidden?: boolean
  21. actionButtonProps?: CommonButtonProps
  22. onAction?(): void
  23. }
  24. const headerElement = useTemplateRef<HTMLElement>('header')
  25. defineExpose({
  26. headerElement,
  27. })
  28. const props = withDefaults(defineProps<Props>(), {
  29. refetch: false,
  30. containerTag: 'header',
  31. })
  32. /**
  33. *Workaround to satisfy linter
  34. * @bug https://github.com/vuejs/language-tools/issues/5082
  35. * Wait to be closed
  36. * */
  37. const slots: SetupContext['slots'] = useSlots()
  38. const hasSlots = computed(() => Object.keys(slots).length > 0)
  39. const headerClass = computed(() => {
  40. return [
  41. 'flex items-center justify-center text-center text-lg font-bold',
  42. props.titleClass,
  43. ]
  44. })
  45. </script>
  46. <template>
  47. <component
  48. :is="containerTag"
  49. v-if="title || backUrl || (onAction && actionTitle) || hasSlots"
  50. ref="header"
  51. class="grid h-[64px] shrink-0 grid-cols-[75px_auto_75px] border-b-[0.5px] border-white/10 bg-black px-4"
  52. data-test-id="appHeader"
  53. >
  54. <div class="flex items-center justify-self-start text-base">
  55. <slot
  56. name="before"
  57. :data="{ backUrl, backTitle, backIgnore, backAvoidHomeButton }"
  58. >
  59. <CommonBackButton
  60. v-if="backUrl"
  61. :fallback="backUrl"
  62. :label="backTitle"
  63. :ignore="backIgnore"
  64. :avoid-home-button="backAvoidHomeButton"
  65. />
  66. </slot>
  67. </div>
  68. <div class="flex flex-1 items-center justify-center">
  69. <CommonRefetch v-bind="defaultAttrs" :refetch="refetch">
  70. <slot :data="{ defaultAttrs, refetch }">
  71. <h1 :class="headerClass">
  72. {{ $t(title) }}
  73. </h1>
  74. </slot>
  75. </CommonRefetch>
  76. </div>
  77. <div
  78. v-if="((onAction || actionTitle) && !actionHidden) || slots.after"
  79. class="flex items-center justify-self-end text-base"
  80. >
  81. <slot name="after" :data="{ actionButtonProps }">
  82. <CommonButton
  83. v-bind="{
  84. variant: 'primary',
  85. transparentBackground: true,
  86. ...actionButtonProps,
  87. }"
  88. @click="onAction?.()"
  89. >
  90. {{ $t(actionTitle) }}
  91. </CommonButton>
  92. </slot>
  93. </div>
  94. </component>
  95. </template>