CommonSectionCollapse.vue 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import { computed } from 'vue'
  4. import { useSessionStore } from '#shared/stores/session.ts'
  5. import CollapseButton from '#desktop/components/CollapseButton/CollapseButton.vue'
  6. import { useCollapseHandler } from '#desktop/components/CollapseButton/composables/useCollapseHandler.ts'
  7. import { useTransitionCollapse } from '#desktop/composables/useTransitionCollapse.ts'
  8. export interface Props {
  9. id: string
  10. title?: string
  11. size?: 'small' | 'large'
  12. noCollapse?: boolean
  13. noNegativeMargin?: boolean
  14. noHeader?: boolean
  15. }
  16. const props = withDefaults(defineProps<Props>(), {
  17. size: 'small',
  18. })
  19. const emit = defineEmits<{
  20. collapse: [boolean]
  21. expand: [boolean]
  22. }>()
  23. const headerId = computed(() => `${props.id}-header`)
  24. const { userId } = useSessionStore()
  25. const { toggleCollapse, isCollapsed } = useCollapseHandler(emit, {
  26. storageKey: `${userId}-${props.id}-section-collapsed`,
  27. })
  28. const { collapseDuration, collapseEnter, collapseAfterEnter, collapseLeave } =
  29. useTransitionCollapse()
  30. </script>
  31. <template>
  32. <!-- eslint-disable vuejs-accessibility/no-static-element-interactions-->
  33. <div class="flex flex-col gap-1">
  34. <header
  35. v-if="!noHeader"
  36. :id="headerId"
  37. class="group flex cursor-default items-center justify-between text-stone-200"
  38. :class="{
  39. 'cursor-pointer rounded-md focus-within:outline focus-within:outline-1 focus-within:outline-offset-1 focus-within:outline-blue-800 hover:bg-blue-600 dark:hover:bg-blue-900 hover:dark:text-white':
  40. !noCollapse,
  41. 'px-1 py-0.5': size === 'small',
  42. '-mx-1': size === 'small' && !noNegativeMargin,
  43. 'px-2 py-2.5': size === 'large',
  44. '-mx-2': size === 'large' && !noNegativeMargin,
  45. }"
  46. @click="!noCollapse && toggleCollapse()"
  47. @keydown.enter="!noCollapse && toggleCollapse()"
  48. >
  49. <slot name="title">
  50. <CommonLabel
  51. class="grow select-none text-current"
  52. :size="size"
  53. tag="h3"
  54. >
  55. {{ $t(title) }}
  56. </CommonLabel>
  57. </slot>
  58. <CollapseButton
  59. v-if="!noCollapse"
  60. :collapsed="isCollapsed"
  61. :owner-id="id"
  62. no-padded
  63. class="focus-visible:bg-transparent focus-visible:text-black group-hover:text-black group-hover:opacity-100 dark:focus-visible:text-white dark:group-hover:text-white"
  64. :class="{ 'opacity-100': isCollapsed }"
  65. orientation="vertical"
  66. @keydown.enter="toggleCollapse()"
  67. />
  68. </header>
  69. <Transition
  70. name="collapse"
  71. :duration="collapseDuration"
  72. @enter="collapseEnter"
  73. @after-enter="collapseAfterEnter"
  74. @leave="collapseLeave"
  75. >
  76. <div v-show="!isCollapsed || noHeader" :id="id">
  77. <slot :header-id="headerId" />
  78. </div>
  79. </Transition>
  80. </div>
  81. </template>