123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
- <script setup lang="ts">
- import { computed, watch } from 'vue'
- import CollapseButton from '#desktop/components/CollapseButton/CollapseButton.vue'
- import { useCollapseHandler } from '#desktop/components/CollapseButton/composables/useCollapseHandler.ts'
- import { useTransitionCollapse } from '#desktop/composables/useTransitionCollapse.ts'
- export interface Props {
- id: string
- title?: string
- size?: 'small' | 'large'
- noCollapse?: boolean
- noNegativeMargin?: boolean
- noHeader?: boolean
- scrollable?: boolean
- }
- const props = withDefaults(defineProps<Props>(), {
- size: 'small',
- })
- const modelValue = defineModel<boolean>({
- default: false,
- })
- const emit = defineEmits<{
- collapse: [boolean]
- expand: [boolean]
- }>()
- const headerId = computed(() => `${props.id}-header`)
- const { toggleCollapse, isCollapsed } = useCollapseHandler(emit)
- const { collapseDuration, collapseEnter, collapseAfterEnter, collapseLeave } =
- useTransitionCollapse()
- watch(
- modelValue,
- (newValue) => {
- if (isCollapsed.value === newValue) return
- isCollapsed.value = newValue
- },
- {
- immediate: true,
- },
- )
- watch(
- isCollapsed,
- (newValue) => {
- if (modelValue.value === newValue) return
- modelValue.value = newValue
- },
- {
- immediate: true,
- },
- )
- </script>
- <template>
- <!-- eslint-disable vuejs-accessibility/no-static-element-interactions-->
- <div class="flex flex-col gap-1" :class="{ 'overflow-y-auto': scrollable }">
- <header
- v-if="!noHeader"
- :id="headerId"
- class="group flex cursor-default items-center justify-between text-stone-200 dark:text-neutral-500"
- :class="{
- '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':
- !noCollapse,
- 'px-1 py-0.5': size === 'small',
- '-mx-1': size === 'small' && !noNegativeMargin,
- 'px-2 py-2.5': size === 'large',
- '-mx-2': size === 'large' && !noNegativeMargin,
- }"
- @click="!noCollapse && toggleCollapse()"
- @keydown.enter="!noCollapse && toggleCollapse()"
- >
- <slot name="title">
- <CommonLabel
- class="grow select-none text-current"
- :size="size"
- tag="h3"
- >
- {{ $t(title) }}
- </CommonLabel>
- </slot>
- <CollapseButton
- v-if="!noCollapse"
- :collapsed="isCollapsed"
- :owner-id="id"
- no-padded
- 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"
- :class="{ 'opacity-100': isCollapsed }"
- orientation="vertical"
- @keydown.enter="toggleCollapse()"
- />
- </header>
- <Transition
- name="collapse"
- :duration="collapseDuration"
- @enter="collapseEnter"
- @after-enter="collapseAfterEnter"
- @leave="collapseLeave"
- >
- <div
- v-show="!isCollapsed || noHeader"
- :id="id"
- :class="{ 'overflow-y-auto': scrollable }"
- >
- <slot :header-id="headerId" />
- </div>
- </Transition>
- </div>
- </template>
|