Benutzt

Ein Kompositionsgerät zum Erstellen von geführten Touren, indem ein einzelner Popover über Stufen hinweg neu verankert wird.

Bearbeiten

Verwenden Sie das automatisch importierte useTour composable, um eine geführte Tour mit einem einzelnen Popover durchzuführen, dessen Anker sich zwischen den Schritten bewegt. Das composable besitzt den Schrittstatus und löst den target jedes Schritts in einen reference auf, den Sie an <UPopover> binden, während Sie die volle Kontrolle über den Inhalt und die Navigation behalten.

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>

Jeder Schritt erfordert ein target, an das der Popover ankettet. Es akzeptiert einen CSS-Selektor, ein Element, ein virtuelles Element (alles mit getBoundingClientRect) oder einen Verweis/Getter, der eines dieser Elemente zurückgibt. Übergeben Sie null, um den Schritt in der Mitte des Viewports zu verankern. Jedes andere Feld in einem Schritt (title, body, side,...) wird unberührt und über current verfügbar übergeben.

<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>
  • Built auf der reaktiven reference-Prop des Popovers, so dass der Popover reibungslos neu positioniert wird, wenn sich der aktive Schritt ändert.
  • Das aktive Ziel wird automatisch in die Ansicht gescrollt, wenn ein Schritt aktiv wird.
  • Da Sie den Inhalt selbst rendern, müssen Sie kein zusätzliches Thema oder Gebietsschema verwalten.

API (englisch)

useTour(steps, options?) nicht

Parameters Bearbeiten

steps
MaybeRefOrGetter<TourStep[]> required
Die Liste der Tour-Schritte. Kann ein statisches Array, ein ref oder ein Getter für reaktive Schritte sein
options
UseTourOptions
Konfigurationsmöglichkeiten für die Tour.

return

open
Ref<boolean>
Die Tour ist derzeit geöffnet.
index
Ref<number>
Der aktuelle Stufenindex, geklemmt an den Schrittbereich.
current
ComputedRef<TourStep | undefined>
Das aktuelle step-Objekt oder undefined, wenn keine steps vorhanden sind.
reference
ComputedRef<ReferenceElement | undefined>
Der aufgelöste Anker für den aktuellen Schritt wird an <UPopover :reference> übergeben.
total
ComputedRef<number>
Die Gesamtzahl der Schritte.
hasNext
ComputedRef<boolean>
Ob es einen nächsten Schritt gibt.
hasPrev
ComputedRef<boolean>
Ob ein vorheriger Schritt existiert.
start
(index?: number) => void
Öffnen Sie die Tour, optional bei einem bestimmten Index.
next
() => void
Gehen Sie zum nächsten Schritt. Schleift oder endet am Ende, abhängig von der loop-Option.
prev
() => void
Gehen Sie zum vorherigen Schritt.
goTo
(index: number) => void
Springen Sie zu einem bestimmten Schritt und öffnen Sie die Tour.
finish
() => void
Schließen Sie die Tour.