Usos

Un componente para construir visitas guiadas volviendo a anclar un solo Popover a través de los pasos.

xph0000xUso

Utilice el componente useTour autoimportado para conducir una visita guiada con un solo Popover cuyo ancla se mueve entre los pasos. El componente posee el estado del paso y resuelve el target de cada paso en un reference que se une a <UPopover>, mientras mantiene el control total sobre el contenido y la navegación.

Dashboard

Overview of your workspace.

Profile

Manage your account.

Settings

Configure your preferences.

<script setup lang="ts">
const dashboard = useTemplateRef('dashboard')
const profile = useTemplateRef('profile')
const settings = useTemplateRef('settings')

const tour = useTour([
  {
    target: () => dashboard.value,
    title: 'Welcome aboard',
    body: 'The popover re-anchors to each target as you step through the tour.'
  },
  {
    target: () => profile.value,
    title: 'Make it yours',
    body: 'You own the content and the buttons — no extra theme or locale to maintain.',
    side: 'right' as const
  },
  {
    target: () => settings.value,
    title: 'You\'re all set',
    body: 'Press Finish to close the tour.'
  }
])
</script>

<template>
  <div class="w-full space-y-4">
    <div class="flex justify-end">
      <UButton icon="i-lucide-wand-sparkles" @click="tour.start()">
        Start tour
      </UButton>
    </div>

    <div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
      <div ref="dashboard" class="p-4 rounded-lg border border-default bg-elevated/50">
        <p class="font-medium text-highlighted">
          Dashboard
        </p>
        <p class="text-sm text-muted">
          Overview of your workspace.
        </p>
      </div>
      <div ref="profile" class="p-4 rounded-lg border border-default bg-elevated/50">
        <p class="font-medium text-highlighted">
          Profile
        </p>
        <p class="text-sm text-muted">
          Manage your account.
        </p>
      </div>
      <div ref="settings" class="p-4 rounded-lg border border-default bg-elevated/50">
        <p class="font-medium text-highlighted">
          Settings
        </p>
        <p class="text-sm text-muted">
          Configure your preferences.
        </p>
      </div>
    </div>

    <UPopover
      :open="tour.open.value"
      :reference="tour.reference.value"
      :content="{ side: tour.current.value?.side, sideOffset: 8 }"
      :dismissible="false"
      arrow
    >
      <template #content>
        <div class="p-4 max-w-xs space-y-2">
          <div class="flex items-center justify-between gap-4">
            <p class="font-semibold text-highlighted">
              {{ tour.current.value?.title }}
            </p>
            <span class="text-xs text-muted tabular-nums">
              {{ tour.index.value + 1 }} / {{ tour.total.value }}
            </span>
          </div>
          <p class="text-sm text-muted">
            {{ tour.current.value?.body }}
          </p>
          <div class="flex items-center justify-between pt-2">
            <UButton
              color="neutral"
              variant="outline"
              size="sm"
              :disabled="!tour.hasPrev.value"
              @click="tour.prev()"
            >
              Back
            </UButton>
            <UButton size="sm" @click="tour.next()">
              {{ tour.hasNext.value ? 'Next' : 'Finish' }}
            </UButton>
          </div>
        </div>
      </template>
    </UPopover>
  </div>
</template>
<script setup lang="ts">
import { useTemplateRef } from 'vue'

const dashboard = useTemplateRef('dashboard')
const profile = useTemplateRef('profile')
const settings = useTemplateRef('settings')

const tour = useTour([
  {
    target: () => dashboard.value,
    title: 'Welcome aboard',
    body: 'The popover re-anchors to each target as you step through the tour.'
  },
  {
    target: () => profile.value,
    title: 'Make it yours',
    body: 'You own the content and the buttons — no extra theme or locale to maintain.',
    side: 'right' as const
  },
  {
    target: () => settings.value,
    title: 'You\'re all set',
    body: 'Press Finish to close the tour.'
  }
])
</script>

<template>
  <div class="w-full space-y-4">
    <div class="flex justify-end">
      <UButton icon="i-lucide-wand-sparkles" @click="tour.start()">
        Start tour
      </UButton>
    </div>

    <div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
      <div ref="dashboard" class="p-4 rounded-lg border border-default bg-elevated/50">
        <p class="font-medium text-highlighted">
          Dashboard
        </p>
        <p class="text-sm text-muted">
          Overview of your workspace.
        </p>
      </div>
      <div ref="profile" class="p-4 rounded-lg border border-default bg-elevated/50">
        <p class="font-medium text-highlighted">
          Profile
        </p>
        <p class="text-sm text-muted">
          Manage your account.
        </p>
      </div>
      <div ref="settings" class="p-4 rounded-lg border border-default bg-elevated/50">
        <p class="font-medium text-highlighted">
          Settings
        </p>
        <p class="text-sm text-muted">
          Configure your preferences.
        </p>
      </div>
    </div>

    <UPopover
      :open="tour.open.value"
      :reference="tour.reference.value"
      :content="{ side: tour.current.value?.side, sideOffset: 8 }"
      :dismissible="false"
      arrow
    >
      <template #content>
        <div class="p-4 max-w-xs space-y-2">
          <div class="flex items-center justify-between gap-4">
            <p class="font-semibold text-highlighted">
              {{ tour.current.value?.title }}
            </p>
            <span class="text-xs text-muted tabular-nums">
              {{ tour.index.value + 1 }} / {{ tour.total.value }}
            </span>
          </div>
          <p class="text-sm text-muted">
            {{ tour.current.value?.body }}
          </p>
          <div class="flex items-center justify-between pt-2">
            <UButton
              color="neutral"
              variant="outline"
              size="sm"
              :disabled="!tour.hasPrev.value"
              @click="tour.prev()"
            >
              Back
            </UButton>
            <UButton size="sm" @click="tour.next()">
              {{ tour.hasNext.value ? 'Next' : 'Finish' }}
            </UButton>
          </div>
        </div>
      </template>
    </UPopover>
  </div>
</template>

Acepta un selector CSS, un elemento, un elemento virtual (cualquier cosa con getBoundingClientRect) o un ref/getter que devuelve uno de esos. Pase null para anclar el paso al centro de la ventana. Cualquier otro campo en un paso (title, body, side,...) se pasa intacto y está disponible a través de current.

<script setup lang="ts">
const card = useTemplateRef('card')

const tour = useTour([
  { target: '#cta', title: 'Get started' },
  { target: () => card.value, title: 'Profile', side: 'right' },
  { target: null, title: 'All set' }
])
</script>

<template>
  <UButton @click="tour.start()">Start tour</UButton>

  <UPopover :open="tour.open.value" :reference="tour.reference.value" :dismissible="false">
    <template #content>
      <!-- your content + buttons -->
      <UButton :disabled="!tour.hasPrev.value" @click="tour.prev()">Back</UButton>
      <UButton @click="tour.next()">{{ tour.hasNext.value ? 'Next' : 'Finish' }}</UButton>
    </template>
  </UPopover>
</template>
  • Construido sobre la hélice reactiva reference del Popover, por lo que el popover se reposiciona suavemente cuando cambia el paso activo.
  • El objetivo activo se desplaza a la vista automáticamente cuando un paso se activa.
  • Dado que usted mismo procesa el contenido, no hay ningún tema o configuración regional adicional que mantener.

API (Edición española)

useTour(steps, options?)xx{lang="ts-type"} (Edición española)

Parámetros

steps
MaybeRefOrGetter<TourStep[]> required
Puede ser una matriz estática, un ref, o un getter para pasos reactivos.
options
UseTourOptions
Opciones de configuración del tour.

Regresar

open
Ref<boolean>
The Tour is currently open.
index
Ref<number>
El índice de pasos actual, fijado al rango de pasos.
current
ComputedRef<TourStep | undefined>
El objeto de paso actual, o undefined cuando no hay pasos.
reference
ComputedRef<ReferenceElement | undefined>
El ancla resuelta para el paso actual, para pasar a <UPopover :reference>.
total
ComputedRef<number>
El número total de pasos.
hasNext
ComputedRef<boolean>
Si existe un siguiente paso.
hasPrev
ComputedRef<boolean>
Si existe un paso anterior.
start
(index?: number) => void
Abra el recorrido, opcionalmente en un índice dado.
next
() => void
Loops o termina al final dependiendo de la opción loop.
prev
() => void
Ir al paso anterior.
goTo
(index: number) => void
Salta a un paso específico y abre el recorrido.
finish
() => void
Cierra el tour.