Blocks

Preview

A block to preview the selected content.

A block to preview the selected content.

Usage

Please note that for MePreview to render correctly, its parent container must have an explicitly defined height. Its default slot is used to compose the list.

Loading preview...
<template>
  <div class="h-[800px] w-full">
    <MePreview class="size-full">
      <template #preview>
        <iframe
          v-if="detailSrc"
          :src="detailSrc"
          class="size-full border-0"
          title="Document preview"
        />
        <div
          v-else
          class="flex size-full flex-col items-center justify-center gap-4 p-8 text-center"
        >
          <h2 class="text-base font-medium text-highlighted">
            Lorem Ipsum Sith Ahmet
          </h2>
          <p class="max-w-sm text-sm text-muted">
            consectetur adipiscing elit duis tristique sollicitudin nibh sit amet
            commodo nulla facilisi nullam vehicula ipsum a arcu.
          </p>
          <div class="flex flex-wrap justify-center gap-2">
            <UButton
              label="Lorem Ipsum"
              color="neutral"
              variant="outline"
            />
            <UButton
              label="Lorem Ipsum"
              color="primary"
            />
          </div>
        </div>
      </template>

      <div class="flex h-full min-h-0 w-full flex-col">
        <div class="min-h-0 flex-1 overflow-y-auto">
          <div
            v-for="row in listRows"
            :key="row.id"
            class="flex w-full cursor-pointer border-b border-accented hover:bg-elevated/50"
            @click="openDetail"
          >
            <div class="min-w-0 flex-1 px-2 py-4">
              <MeForehead>
                <template #leading>
                  <div class="flex flex-col gap-1">
                    <div class="flex items-start justify-between gap-2">
                      <p class="mb-0.5 flex min-w-0 flex-1 flex-wrap items-center gap-1 font-semibold">
                        <span>{{ row.title }}</span>
                        <UBadge
                          v-if="row.badge"
                          :label="row.badge"
                          color="primary"
                          variant="subtle"
                          size="sm"
                        />
                      </p>
                      <UButton
                        color="neutral"
                        variant="ghost"
                        size="sm"
                        icon="i-lucide-ellipsis-vertical"
                        class="shrink-0"
                        aria-label="Row actions"
                        @click.stop
                      />
                    </div>
                    <div class="flex flex-col gap-1 text-xs text-muted">
                      <p
                        v-for="(line, lineIndex) in row.lines"
                        :key="lineIndex"
                        class="flex flex-wrap items-center gap-1"
                      >
                        <template v-if="typeof line === 'string'">
                          {{ line }}
                        </template>
                        <template v-else>
                          <span>{{ line.text }}</span>
                          <UButton
                            v-if="line.showMessages"
                            color="neutral"
                            variant="ghost"
                            size="xs"
                            icon="i-lucide-messages-square"
                            class="-my-1"
                            aria-label="Open messages"
                          />
                          <UBadge
                            v-if="line.extraBadge"
                            :label="line.extraBadge"
                            color="primary"
                            variant="subtle"
                            size="xs"
                            class="shrink-0"
                          />
                        </template>
                      </p>
                      <p
                        v-if="row.links?.length"
                        class="flex flex-wrap items-center gap-1"
                      >
                        <template
                          v-for="(link, linkIndex) in row.links"
                          :key="link.href"
                        >
                          <a
                            :href="link.href"
                            class="text-info"
                            @click.stop
                          >{{ link.label }}</a>
                          <USeparator
                            v-if="row.links && linkIndex < row.links.length - 1"
                            orientation="vertical"
                            class="h-3"
                          />
                        </template>
                      </p>
                    </div>
                  </div>
                </template>
              </MeForehead>
            </div>
          </div>
        </div>
      </div>
    </MePreview>
  </div>
</template>

<script setup lang="ts">
const detailSrc = ref()

const listRows = [
  {
    id: '1',
    title: '4356776543 - Requisição de parafusos',
    badge: 'RFI',
    lines: [
      'Criado em 19/08/2026',
      'Categoria RFI - Responsible Buyer: Marcel Lottito',
      '(11) 98329911 | (12) 24563946'
    ],
    links: [
      { label: 'john.doe@me.com.br', href: 'mailto:john.doe@me.com.br' },
      { label: 'www.site.com.br', href: 'https://www.site.com.br' }
    ]
  },
  {
    id: '2',
    title: '24729602 - Mercado Eletrônico SA',
    lines: [
      {
        text: 'Criado em 22/07/2025 17:59:30 | Iker Buruaga',
        showMessages: true
      },
      {
        text: 'Avenida Engenheiro Alberto Massa, 432 CJ 99 – Centro, São Paulo – SP, CEP 01234-567',
        extraBadge: '+2'
      },
      '(11) 98329911 | (12) 24563946 | (12) 24563946 | (12) 24563946'
    ],
    links: [
      { label: 'john.doe@me.com.br', href: 'mailto:john.doe@me.com.br' },
      { label: 'www.site.com.br', href: 'https://www.site.com.br' }
    ]
  },
  {
    id: '3',
    title: 'ME_4600068466 - Mercado Eletrônico SA',
    lines: ['Flavio Cabral', '(11) 983857412'],
    links: [{ label: 'flavio.cabral@me.com.br', href: 'mailto:flavio.cabral@me.com.br' }]
  },
  {
    id: '4',
    title: '1234567891 - Cotação',
    badge: 'Categoria: RFX',
    lines: [
      'Criado por Administrador FAST1',
      'Criado em 24/02/2026',
      'Data Limite para Resposta 12/11/2025 09:39:44'
    ],
    links: [
      { label: 'john.doe@me.com.br', href: 'mailto:john.doe@me.com.br' },
      { label: 'www.site.com.br', href: 'https://www.site.com.br' }
    ]
  },
  {
    id: '5',
    title: '14499712 - Mercado Eletrônico',
    lines: [
      'CNPJ: 07.418.787/0001-55',
      'Avenida Engenheiro Alberto Massa, 432 CJ 99 – Centro, São Paulo – SP, CEP 01234-567',
      '(11) 98329911 | (12) 24563946'
    ],
    links: [
      { label: 'john.doe@me.com.br', href: 'mailto:john.doe@me.com.br' },
      { label: 'www.site.com.br', href: 'https://www.site.com.br' }
    ]
  }
]

function openDetail() {
  detailSrc.value = '/'
}
</script>

Preview

By default, the component uses an iframe for content preview via the previewConfig prop. The preview slot allows you to override the iframe and customize the visualization.

type previewConfig = {
  allow?: string,
  loading?: 'eager' | 'lazy',
  referrerpolicy?: 'no-referrer' | 'no-referrer-when-downgrade' | 'origin' | 'origin-when-cross-origin' | 'same-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url',
  name?: string,
  src: string
}

Card 1

Card 2

Card 3

Loading...
<template>
  <div class="w-full h-[342px]">
    <MePreview
      :preview-config="previewConfig"
      class="w-full"
    >
      <div
        v-for="i in 3"
        :class="[
          'h-[114px] w-full border border-accented py-4 px-2 cursor-pointer',
          { 'border-b-0': i !== 3 }
        ]"
      >
        {{ `Card ${i}` }}
      </div>
    </MePreview>
  </div>
</template>

<script setup lang="ts">
const previewConfig = { src: '#' }
</script>

Loading

When the loading prop is true, the component enters its default loading state. You can use the loading slot to customize this state.

Card 1

Card 2

Card 3

Loading...
<template>
  <div class="w-full h-[342px]">
    <MePreview
      class="w-full"
      loading
    >
      <div
        v-for="i in 3"
        :class="[
          'h-[114px] w-full border border-accented py-4 px-2 cursor-pointer',
          { 'border-b-0': i !== 3 }
        ]"
      >
        {{ `Card ${i}` }}
      </div>
    </MePreview>
  </div>
</template>

Empty

If no content preview is provided by the previewConfig prop or the preview slot, the component enters its default empty state. Use the empty slot to customize this state.

Card 1

Card 2

Card 3

Empty state illustration

Select an item to read

Nothing was selected
<template>
  <div class="w-full h-[342px]">
    <MePreview class="w-full">
      <div
        v-for="i in 3"
        :class="[
          'h-[114px] w-full border border-accented py-4 px-2 cursor-pointer',
          { 'border-b-0': i !== 3 }
        ]"
      >
        {{ `Card ${i}` }}
      </div>
    </MePreview>
  </div>
</template>

API

Props

PropDefaultType
previewConfig{ allow, loading, name, referrerpolicy, src }
Preview configuration options
loadingBoolean
Loading state

Slots

SlotType
default{}
loading{}
empty{}