๐ŸŸข Accordion


Basic

NAccordion is a component that allows you to display collapsible content.

NAccordion label is wrapped around the NButton component, which means that all the props and slots of NButton are available to use.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.
<script setup lang="ts">
const items = [
  {
    label: 'How do I get started?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    defaultOpen: true,
  },
  {
    label: 'What is your return policy?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'Can I exchange an item?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
]
</script>

<template>
  <NAccordion
    :items="items"
  />
</template>

Mounted

โšก By default, the accordion's content is not rendered until it is opened for performance reasons. To render the content when the page loads, even if the accordion is closed for SEO purposes, use the mounted prop.

mounted - allows you to set the mount state of all items.

item.mount - allows you to set the mount state of a specific item. This will override the mounted prop.

If you have a lot of accordion items, it is not recommended to use the mounted prop because it affects the performance of the page, instead use the item.mount prop to mount the content of a specific item if needed.
<script setup lang="ts">
const items = [
  {
    label: 'How do I get started?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'What is your return policy?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'Can I exchange an item?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'When will I get my refund?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    mounted: false,
  },
]
</script>

<template>
  <NAccordion
    :items="items"
    mounted
  />
</template>

Multiple

multiple - allows you to expand multiple items at the same time.

<script setup lang="ts">
const items = [
  {
    label: 'How do I get started?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'What is your return policy?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'Can I exchange an item?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
]
</script>

<template>
  <NAccordion
    :items="items"
    multiple
  />
</template>

Default open

default-open - opens all items by default.

item.defaultOpen - allows you to control the default open state of a specific item.

Use multiple prop when using default-open to open multiple items by default.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.
<script setup lang="ts">
const items = [
  {
    label: 'This is open by default',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'This is open by default',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'This is close by default',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    defaultOpen: false, // priority over defaultOpen prop
  },
]
</script>

<template>
  <NAccordion
    :items="items"
    multiple
    default-open
  />
</template>

Color

Since we use the NButton component for the accordion label, you can use the btn prop to change the color of the label. See NButton for more information.

btn="text-{color}" prop is basically from NButton component. You can use it to change the color of the label.


<script setup lang="ts">
const items = [
  {
    label: 'How do I get started?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'What is your return policy?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'Can I exchange an item?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
]
</script>

<template>
  <div flex="~ col" space-y-4>
    <NAccordion
      :items="items"
      btn="text-gray"
    />

    <hr border="base">

    <NAccordion
      :items="items"
      btn="text-orange"
    />

    <hr border="base">

    <NAccordion
      :items="items"
      btn="text-indigo"
    />
  </div>
</template>

Icon

leading - allows you to add leading icon to the label.

item.leading - allows you to add leading icon to the label of a specific item.

trailingOpen - allows you to customize trailing open icon.

trailingClose - allows you to customize trailing close icon.

If no trailing icon is given, it will be used for both open and close states and will animate upside down automatically.
By default, we use the heroicons and tabler for the icons, you can use any icon provided by Iconify through icones, refer to configuration for more information.
Custom Global leading icons

Custom per item leading icon

Custom Trailing open icon

Custom Trailing open and close icons
<script setup lang="ts">
const items1 = [
  {
    label: 'How do I get started?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'What is your return policy?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'Can I exchange an item?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
]

const items2 = [
  {
    label: 'How do I get started?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    leading: 'i-heroicons-information-circle text-info',
  },
  {
    label: 'What is your return policy?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    leading: 'i-heroicons-shield-check text-green-600',
  },
  {
    label: 'Can I exchange an item?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    leading: 'i-heroicons-shopping-cart text-amber',
  },
]
</script>

<template>
  <div flex="~ col" gap-4>
    <span class="text-sm font-medium">
      Custom Global leading icons
    </span>

    <NAccordion
      :items="items1"
      leading="i-heroicons-question-mark-circle"
    />

    <hr border-base>

    <span class="text-sm font-medium">
      Custom per item leading icon
    </span>

    <NAccordion
      :items="items2"
    />

    <hr border-base>

    <span class="text-sm font-medium">
      Custom Trailing open icon
    </span>

    <NAccordion
      :items="items1"
      trailing-open="i-heroicons-chevron-left"
      :una="{
        accordionTrailingClose: '-rotate-90',
        accordionTrailingOpen: 'rotate-0',
      }"
    />

    <hr border-base>

    <span class="text-sm font-medium">
      Custom Trailing open and close icons
    </span>

    <NAccordion
      :items="items1"
      trailing-open="i-twemoji-smiling-face-with-sunglasses"
      trailing-close="i-twemoji-loudly-crying-face"
    />
  </div>
</template>

Reverse

reverse - allows you to switch the position of the trailing and leading icons.

item.reverse - allows you to switch the position of the trailing and leading icons of a specific item.

reverse prop is basically from NButton component. You can use it to switch the position of the trailing and leading icons.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.
<script setup lang="ts">
const items = [
  {
    label: 'This will be reversed',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    defaultOpen: true,
  },
  {
    label: 'This will be reversed',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'This will not be reversed',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    reverse: false,
  },
]
</script>

<template>
  <NAccordion
    :items="items"
    leading="i-heroicons-question-mark-circle"
    reverse
  />
</template>

Unstyle mode

unstyle - removes the default border, padding, and divider of the accordion.



<script setup lang="ts">
const items = [
  {
    label: 'Why Una',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    leading: 'i-heroicons-book-open',
  },
  {
    label: 'Getting Started',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    leading: 'i-heroicons-information-circle',
  },
  {
    label: 'Installation',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    leading: 'i-heroicons-arrow-down-tray',
  },
  {
    label: 'Themes and Colors',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    leading: 'i-heroicons-beaker',
    disabled: true,
  },
  {
    label: 'Components',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    leading: 'i-heroicons-square-3-stack-3d',
    loading: true,
  },
]
</script>

<template>
  <div flex="~ col" gap-4>
    <NAccordion
      unstyle
      :items="items"
    />

    <hr border-base>

    <NAccordion
      unstyle
      :items="items"
      btn="ghost-lime"
    />

    <hr border-base>

    <NAccordion
      unstyle
      :items="items"
      btn="solid-orange"
      :una="{
        accordionButton: 'rounded-none',
        accordionPanel: 'px-0',
      }"
    />
  </div>
</template>

Custom

Customize the accordion with your own styles using una preset configuration.

You can globally customize the accordion preset if want to have a different default style. See Configuration section for more details.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.
<script setup lang="ts">
const items = [
  {
    label: 'How do I get started?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    defaultOpen: true,
  },
  {
    label: 'What is your return policy?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'Can I exchange an item?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
]
</script>

<template>
  <NAccordion
    :items="items"
    class="rounded-none bg-muted"
    reverse
    trailing-open="i-heroicons-chevron-right"
    :una="{
      accordionPanel: 'bg-base',
      accordionTrailingClose: 'rotate-90',
      accordionTrailingOpen: 'rotate-0',
    }"
  />
</template>

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.
<script setup lang="ts">
const items = [
  {
    label: '1. How do I get started?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    defaultOpen: true,
  },
  {
    label: '2. What is your return policy?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: '3. Can I exchange an item?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
]
</script>

<template>
  <NAccordion
    :items="items"
    unstyle
    trailing-open="i-heroicons-plus"
    :una="{
      accordionButton: 'px-0',
      accordionPanel: 'bg-muted rounded-none mt-2',
      accordionTrailingClose: '-rotate-45',
      accordionTrailing: 'text-md',
    }"
  />
</template>

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.
<script setup lang="ts">
const items = [
  {
    label: '1. How do I get started?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
    defaultOpen: true,
  },
  {
    label: '2. What is your return policy?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: '3. Can I exchange an item?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
]
</script>

<template>
  <NAccordion
    :items="items"
    unstyle
    btn="~"
    trailing-close="i-heroicons-plus"
    trailing-open="i-heroicons-minus"
    :una="{
      accordionButton: 'px-0',
      accordionTrailing: 'text-md',
    }"
  />
</template>

Slots

If you have a more complex content, you can use slots to customize the accordion.

label - allows you to customize the label of the accordion.

content - allows you to customize the content of the accordion.

Slot propsDescription
indexallows you to access index of the item.
itemallows you to access the item properties.
openallows you to access the open state of the item.
closeallows you to access the close state of the item.
<script setup lang="ts">
const items = [
  {
    label: 'How do I get started?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'What is your return policy?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
  {
    label: 'Can I exchange an item?',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel urna vitae lectus aliquet mollis et eget risus.',
  },
]
</script>

<template>
  <NAccordion
    :items="items"
    unstyle
  >
    <template #label="{ item, index, open }">
      <NButton
        btn="solid-gray block"
        :class="[
          open ? 'ring-0 rounded-b-none border border-primary border-b-0' : '',
        ]"
        class="justify-between gap-x-3 py-3"
        :label="`${item.label}`"
      >
        <template #leading>
          <div class="h-6 w-6 flex items-center justify-center rounded-full bg-inverted">
            <span text="inverted sm">{{ index + 1 }}</span>
          </div>
        </template>

        <template #trailing>
          <NIcon
            name="i-heroicons-chevron-left-20-solid"
            class="ms-auto h-5 w-5 transform transition-transform duration-200"
            :class="[open && '-rotate-90']"
          />
        </template>
      </NButton>
    </template>

    <template #content="{ item, open, close }">
      <div
        p="x-4 y-6"
        flex="~ col"
        :class="[
          open ? 'border border-primary border-t-0' : '',
        ]"
      >
        <p>
          {{ item.content }}
        </p>

        <div mt-5 text-right>
          <NButton
            btn="solid-gray"
            class="mt-3"
            label="Close"
            @click="close"
          />
        </div>
      </div>
    </template>
  </NAccordion>
</template>

index number - allows you to customize the specific item of the accordion. See the example below.

If you want to customize the content of a specific item, make sure not to provide a content slot.
<script setup lang="ts">
const items = [
  {
    label: 'This is index 0',
  },
  {
    label: 'This is index 1',
  },
  {
    label: 'This is index 2',
  },
  {
    label: 'This will render default content slot',
    content: 'Star us on Github!',
  },
]
</script>

<template>
  <NAccordion
    :items="items"
  >
    <template #0>
      <div grid place-items-center px-2 pb-8>
        <NIcon name="i-logos-vue" text-8xl />
      </div>
    </template>

    <template #1>
      <div grid place-items-center px-2 pb-8>
        <NIcon name="i-logos-react" text-8xl />
      </div>
    </template>

    <template #2>
      <div grid place-items-center px-2 pb-8>
        <NIcon name="i-logos-svelte-icon" text-8xl />
      </div>
    </template>
  </NAccordion>
</template>

Props

import type { NButtonProps } from './button'

export interface NAccordionProps extends Omit<NButtonProps, 'una'> {
  /**
   * Allows you to add `UnaUI` accordion preset properties,
   * Think of it as a shortcut for adding options or variants to the preset if available.
   *
   * By default, we don't add any options or variants to the accordion,
   * But you can add your own in the configuration file.
   */
  accordion?: string
  /**
   * Update leading icon when accordion button item is open,
   * Accepts icon name and utility classes
   */
  trailingOpen?: string
  /**
   * Update leading icon when accordion button item is closed,
   * Accepts icon name and utility classes
   */
  trailingClose?: string

  /**
   * Allow multiple accordion items to be open at the same time
   *
   * @default false
   */
  multiple?: boolean
  /**
   * Allow accordion item to be open by default
   *
   * @default false
   */
  defaultOpen?: boolean
  /**
   * Removes border and divider from accordion
   *
   * @default false
   */
  unstyle?: boolean
  /**
   * By default, the accordion is unmounted for performance reasons,
   * This means that the accordion will not be rendered until it is opened,
   * If you want to render the accordion when the page loads, you can use the `mounted` prop.
   *
   * @default false
   */
  mounted?: boolean

  /**
   * List of items to be rendered,
   * It extends the `NButtonProps` interface
   *
   * @see https://github.com/una-ui/una-ui/blob/main/packages/nuxt/src/runtime/types/button.ts
   */
  items: NAccordionItemProps[]

  /**
   * `UnaUI` preset configuration
   *
   * @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/accordion.ts
   */
  una?: {
    accordion?: string
    accordionItem?: string
    accordionButton?: string
    accordionPanel?: string
    accordionLeading?: string
    accordionTrailing?: string
    accordionTrailingOpen?: string
    accordionTrailingClose?: string
    accordionEnterActive?: string
    accordionLeaveActive?: string
  } & NButtonProps['una']
}

export interface NAccordionItemProps extends NButtonProps {
  /**
   * Accordion item content
   */
  content?: string
  /**
   * Update item leading icon when accordion button item is open,
   * Accepts icon name and utility classes
   *
   * @example
   * trailingOpen='i-heroicons-information-circle text-info'
   */
  trailingOpen?: string
  /**
   * Update item leading icon when accordion button item is closed,
   * Accepts icon name and utility classes
   *
   * @example
   * trailingClose='i-heroicons-information-circle text-info'
   */
  trailingClose?: string
  /**
   * Allow accordion item to be open by default
   *
   * @default false
   */
  defaultOpen?: boolean
  /**
   * Close other accordion items when item is open
   *
   * @default false
   */
  closeOthers?: boolean
  /**
   * By default, all the accordion item is unmounted for performance reasons,
   * You can use the `mounted` prop to render the accordion specific on item.
   *
   * @default false
   */
  mounted?: boolean
  /**
   * Allow dynamic attributes to be added to the accordion item,
   *
   */
  [key: string]: any
}

Presets

type AccordionPrefix = 'accordion'

export const staticAccordion: Record<`${AccordionPrefix}-${string}` | AccordionPrefix, string> = {
  // config
  'accordion-trailing-icon': 'i-heroicons-chevron-up',
  'accordion-button-padding': 'p-(x-3 y-4)',
  'accordion-button-default-variant': 'btn-text',
  'accordion-divider': 'divide-(y base)',
  'accordion-border': 'border-(~ base) rounded-md',

  // base
  'accordion': 'flex-(~ col) relative w-full',
  'accordion-item': 'w-full',
  'accordion-button': 'justify-start',
  'accordion-panel': 'text-(muted 0.875em) border-(t $c-divider) accordion-button-padding',
  'accordion-leading': 'text-1.2em',
  'accordion-trailing': 'flex transition items-center text-1em duration-300',
  'accordion-label': 'flex w-full text-1em',

  // trailing transition
  'accordion-trailing-open': '-rotate-180',
  'accordion-trailing-close': 'rotate-0',

  // panel transition
  'accordion-enter-active': 'overflow-hidden transition-height duration-300',
  'accordion-leave-active': 'overflow-hidden transition-height duration-300',
}

export const dynamicAccordion = [
]

export const accordion = [
  ...dynamicAccordion,
  staticAccordion,
]

Component

<script setup lang="ts">
import {
  Disclosure,
  DisclosureButton,
  DisclosurePanel,
} from '@headlessui/vue'
import { createReusableTemplate } from '@vueuse/core'
import { computed, ref } from 'vue'

import type { NAccordionItemProps, NAccordionProps } from '../../types'
import { pickProps } from '../../utils'
import NIcon from './Icon.vue'
import NButton from './Button.vue'

const props = withDefaults(defineProps<NAccordionProps>(), {
  trailingOpen: 'accordion-trailing-icon',
  loadingPlacement: 'trailing',
})

const buttonRefs = ref<(() => void)[]>([])

function closeOthers(index: number) {
  if (props.multiple && !props.items[index].closeOthers)
    return

  buttonRefs.value
    .filter((_, i) => i !== index)
    .forEach(close => close())
}

function onEnter(element: Element, done: () => void) {
  const el = element as HTMLElement
  el.style.height = '0'
  el.style.height = `${element.scrollHeight}px`
  el.addEventListener('transitionend', done, { once: true })
}

function onAfterEnter(element: Element) {
  const el = element as HTMLElement
  el.style.height = 'auto'
}

function onBeforeLeave(element: Element) {
  const el = element as HTMLElement
  el.style.height = `${el.scrollHeight}px`
}

function onLeave(element: Element, done: () => void) {
  const el = element as HTMLElement
  el.style.height = '0'

  el.addEventListener('transitionend', done, { once: true })
}

const [DefineTemplate, ReuseTemplate] = createReusableTemplate<{
  item: NAccordionItemProps
  index: number
  open: boolean
  close: () => void
}>()

const pickedProps = pickProps(props, ['reverse', 'icon', 'btn', 'label', 'leading', 'loading', 'loadingPlacement', 'una', 'trailing', 'leading', 'to', 'type', 'disabled'])

function mergedProps(itemProps: NAccordionItemProps) {
  return Object.assign(pickedProps, itemProps)
}

// TODO: refactor this to sync with NButton variants
const btnVariants = ['solid', 'outline', 'soft', 'ghost', 'link', 'text'] as const
const hasVariant = computed(() => btnVariants.some(btnVariants => props.btn?.includes(btnVariants)))
const isBaseVariant = computed(() => props.btn?.includes('~'))
</script>

<template>
  <div
    :accordion="accordion"
    class="accordion"
    :class="[
      unstyle ? 'space-y-3' : 'accordion-(border divider)',
      una?.accordion,
    ]"
  >
    <DefineTemplate v-slot="{ item, close, index, open }">
      <slot
        :name="item.content ? 'content' : index"
        :item="item" :index="index" :open="open" :close="close"
      >
        <div
          accordion="panel"
          :class="[
            una?.accordionPanel,
            { 'border-t-0': unstyle },
          ]"
        >
          {{ item.content }}
        </div>
      </slot>
    </DefineTemplate>

    <Disclosure
      v-for="(item, i) in items"
      :key="i"
      v-slot="{ open, close }"
      as="div"
      accordion="item"
      :default-open="item.defaultOpen ?? defaultOpen"
      :class="una?.accordionItem"
    >
      <DisclosureButton
        :ref="() => (buttonRefs[i] = close)"
        as="template"
        :disabled="item.disabled ?? disabled"
        @click="closeOthers(i)"
      >
        <slot name="label" :item="item" :index="i" :open="open" :close="close">
          <NButton
            v-bind="mergedProps(item)"
            :btn="`~ block ${btn ?? ''}`"
            :class="[
              { 'accordion-button-default-variant': !hasVariant && !isBaseVariant },
              { 'accordion-button-padding': !unstyle },
              una?.accordionButton,
            ]"
            accordion="button"
            :una="{
              btnLabel: 'accordion-label',
            }"
          >
            <template #leading>
              <!-- TODO: fix conditional statement -->
              <NIcon
                v-if="leading || item.leading"
                accordion="leading"
                :class="una?.accordionLeading"
                :name="item.leading ?? leading ?? ''"
                aria-hidden="true"
              />
            </template>

            <template #trailing>
              <span
                v-if="trailingOpen || trailingClose"
                accordion="trailing"
                :class="[
                  trailingClose || (!trailingClose && open)
                    ? una?.accordionTrailingClose ?? 'accordion-trailing-close'
                    : una?.accordionTrailingOpen ?? 'accordion-trailing-open',
                  una?.accordionTrailing,
                ]"
              >
                <NIcon
                  v-if="(open || !trailingClose) && trailingOpen"
                  :name="trailingOpen"
                  aria-hidden="true"
                />
                <NIcon
                  v-else-if="!open && trailingClose"
                  :name="trailingClose"
                  aria-hidden="true"
                />
              </span>
            </template>
          </NButton>
        </slot>
      </DisclosureButton>

      <Transition
        :enter-active-class="una?.accordionLeaveActive ?? 'accordion-leave-active'"
        :leave-active-class="una?.accordionEnterActive ?? 'accordion-enter-active'"
        @enter="onEnter"
        @after-enter="onAfterEnter"
        @before-leave="onBeforeLeave"
        @leave="onLeave"
      >
        <DisclosurePanel v-if="!item.mounted ?? !mounted">
          <ReuseTemplate
            v-bind="{
              item,
              index: i,
              open,
              close,
            }"
          />
        </DisclosurePanel>
        <DisclosurePanel
          v-else
          v-show="open"
          static
        >
          <ReuseTemplate
            v-bind="{
              item,
              index: i,
              open,
              close,
            }"
          />
        </DisclosurePanel>
      </Transition>
    </Disclosure>
  </div>
</template>