CommonSectionCollapse.vue 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  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. scrollable?: boolean
  16. }
  17. const props = withDefaults(defineProps<Props>(), {
  18. size: 'small',
  19. })
  20. const emit = defineEmits<{
  21. collapse: [boolean]
  22. expand: [boolean]
  23. }>()
  24. const headerId = computed(() => `${props.id}-header`)
  25. const { userId } = useSessionStore()
  26. const { toggleCollapse, isCollapsed } = useCollapseHandler(emit, {
  27. storageKey: `${userId}-${props.id}-section-collapsed`,
  28. })
  29. const { collapseDuration, collapseEnter, collapseAfterEnter, collapseLeave } =
  30. useTransitionCollapse()
  31. </script>
  32. <template>
  33. <!-- eslint-disable vuejs-accessibility/no-static-element-interactions-->
  34. <div class="flex flex-col gap-1" :class="{ 'overflow-y-auto': scrollable }">
  35. <header
  36. v-if="!noHeader"
  37. :id="headerId"
  38. class="group flex cursor-default items-center justify-between text-stone-200 dark:text-neutral-500"
  39. :class="{
  40. '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 hover:text-black dark:hover:bg-blue-900 hover:dark:text-white':
  41. !noCollapse,
  42. 'px-1 py-0.5': size === 'small',
  43. '-mx-1': size === 'small' && !noNegativeMargin,
  44. 'px-2 py-2.5': size === 'large',
  45. '-mx-2': size === 'large' && !noNegativeMargin,
  46. }"
  47. @click="!noCollapse && toggleCollapse()"
  48. @keydown.enter="!noCollapse && toggleCollapse()"
  49. >
  50. <slot name="title">
  51. <CommonLabel
  52. class="grow select-none text-current"
  53. :size="size"
  54. tag="h3"
  55. >
  56. {{ $t(title) }}
  57. </CommonLabel>
  58. </slot>
  59. <CollapseButton
  60. v-if="!noCollapse"
  61. :collapsed="isCollapsed"
  62. :owner-id="id"
  63. no-padded
  64. 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"
  65. :class="{ 'opacity-100': isCollapsed }"
  66. orientation="vertical"
  67. @keydown.enter="toggleCollapse()"
  68. />
  69. </header>
  70. <Transition
  71. name="collapse"
  72. :duration="collapseDuration"
  73. @enter="collapseEnter"
  74. @after-enter="collapseAfterEnter"
  75. @leave="collapseLeave"
  76. >
  77. <div
  78. v-show="!isCollapsed || noHeader"
  79. :id="id"
  80. :class="{ 'overflow-y-auto': scrollable }"
  81. >
  82. <slot :header-id="headerId" />
  83. </div>
  84. </Transition>
  85. </div>
  86. </template>