Vue-API-Referenz

Die vollständige Referenz für die Komponenten, Composables und Hilfsfunktionen von @reelkit/vue.

Reel

Tag: <Reel>

Props

ReelProps

PropTypStandardBeschreibung
countnumberPflichtGesamtzahl der Slides
direction'vertical' | 'horizontal''vertical'Scrollrichtung
size[number, number] | undefinedundefinedBreite und Höhe als [width, height]. Fehlt es, wird per ResizeObserver automatisch gemessen
initialIndexnumber0Index des ersten Slides
loopbooleanfalseSchaltet die Endlosschleife ein
transitionTransitionTransformFnslideTransitionFunktion für den Übergangseffekt. Eingebaut: slideTransition, fadeTransition, flipTransition, cubeTransition, zoomTransition
transitionDurationnumber300Dauer der Animation in ms
swipeDistanceFactornumber0.12Schwelle für das Wischen (0-1)
enableGesturesbooleantrueSchaltet die Navigation per Touch und Ziehen mit der Maus ein
enableNavKeysbooleantrueSchaltet die Navigation mit den Pfeiltasten ein
enableWheelbooleanfalseSchaltet die Navigation mit dem Mausrad ein
wheelDebounceMsnumber200Debounce für Mausrad-Events in ms
rangeExtractor(index: number, count: number) => number[]defaultRangeExtractorEigene Funktion, die bestimmt, welche Indizes gerendert werden
keyExtractor(index: number, indexInRange: number) => stringindex => index.toString()Eigene Key-Funktion für das Rendern der Slides (nützlich mit loop)
ariaLabelstringundefinedZugängliche Bezeichnung für die Karussell-Region
reelStyleRecord<string, string | number>undefinedInline-Styles für das Wurzelelement des Containers
reelClassstring | Array | ObjectundefinedCSS-Klasse(n) für das Wurzelelement des Containers
onNavKeyPress(increment: -1 | 1) => voidundefinedCallback-Prop, das die Standardnavigation mit ArrowUp/ArrowDown ersetzt. Ist es gesetzt, implementierst du die Navigation selbst (zum Beispiel mit reelRef.value.next()). Lass es weg für das Standardverhalten.

Events

EventDatenBeschreibung
beforeChange(index: number, nextIndex: number, indexInRange: number)Wird gesendet, bevor der Übergang zum Slide beginnt
afterChange(index: number, indexInRange: number)Wird gesendet, nachdem der Übergang zum Slide fertig ist
slideDragStart(index: number)Wird gesendet, wenn eine Ziehgeste beginnt
slideDragEnd(index: number)Wird gesendet, wenn eine Ziehgeste endet (losgelassen)
slideDragCanceled(index: number)Wird gesendet, wenn eine Ziehgeste abgebrochen wird (Zurückschnappen)
tap(event: GestureCommonEvent)Wird bei einem einfachen Tippen gesendet
doubleTap(event: GestureCommonEvent)Wird bei einem doppelten Tippen gesendet
longPress(event: GestureCommonEvent)Wird gesendet, wenn ein langes Drücken beginnt
longPressEnd(event: GestureEvent)Wird gesendet, wenn ein langes Drücken endet

Slots

vue-html
<Reel :count="items.length">
  <template #item="{ index, indexInRange, size }">
    <!-- index       : number         — absolute slide index (0 to count-1) -->
    <!-- indexInRange : number         — position in the visible window (0, 1, or 2) -->
    <!-- size         : [number,number] — [width, height] of the container -->
    <MySlide :index="index" :size="size" />
  </template>

  <!-- default slot: overlay content rendered on top of the slides -->
  <ReelIndicator />
</Reel>
SlotScope-PropsBeschreibung
#item{ index: number, indexInRange: number, size: [number, number] }Rendert jeden sichtbaren Slide. Wird für jeden Index im virtualisierten Bereich aufgerufen
defaultnoneOverlay-Inhalt über allen Slides (Indikatoren, Bedienelemente und so weiter)

ReelExpose

Die imperative API, bereitgestellt über ein Template-Ref:

vue
<script setup lang="ts">
import { ref } from 'vue';
import { Reel, type ReelExpose } from '@reelkit/vue';

const reelRef = ref<ReelExpose | null>(null);

function prev()  { reelRef.value?.prev(); }
function next()  { reelRef.value?.next(); }
function jump(i: number) { reelRef.value?.goTo(i, true); }
</script>

<template>
  <Reel ref="reelRef" :count="100">
    <template #item="{ index }">
      <div>Slide {{ index }}</div>
    </template>
  </Reel>
</template>
MethodeTypBeschreibung
next()() => voidGeht zum nächsten Slide
prev()() => voidGeht zum vorherigen Slide
goTo(index, animate?)(number, boolean?) => Promise<void>Navigiert zu einem bestimmten Slide-Index
adjust()() => voidBerechnet die Positionen der Slides neu (nützlich nach einer Layoutänderung)
observe()() => voidBeginnt, auf Gesten, Tastatur und Mausrad zu hören
unobserve()() => voidHört auf, auf Gesten, Tastatur und Mausrad zu hören

ReelIndicator

Tag: <ReelIndicator>

Props

ReelIndicatorProps

PropTypStandardBeschreibung
countnumber | undefinedautoGesamtzahl der Elemente. Verbindet sich innerhalb eines Reel selbst mit dessen Kontext; übergib den Wert ausdrücklich, wenn du die Komponente allein nutzt
activenumber | undefinedautoAktuell aktiver Index. Verbindet sich innerhalb eines Reel selbst mit dessen Kontext; übergib den Wert ausdrücklich, wenn du die Komponente allein nutzt
direction'vertical' | 'horizontal''vertical'Ausrichtung des Indikators
radiusnumber3Radius der Punkte in Pixeln
visiblenumber5Höchstzahl gleichzeitig sichtbarer Punkte in normaler Größe
gapnumber4Abstand zwischen den Punkten in Pixeln
activeColorstring'#fff'Farbe des aktiven Punkts
inactiveColorstring'rgba(255, 255, 255, 0.5)'Farbe der inaktiven Punkte
edgeScalenumber0.5Skalierung der überzähligen Punkte am Rand
onDotClick(index: number) => voidundefinedEigener Klick-Handler. Fehlt er innerhalb eines Reel, navigiert ein Klick standardmäßig zum Index des Punkts
indicatorClassstring | Array | ObjectundefinedCSS-Klasse(n) für das Wurzelelement der Tablist
indicatorStyleCSSPropertiesundefinedInline-Styles, die in das Wurzelelement der Tablist übernommen werden

Events

EventDatenBeschreibung
dotClick(index: number)Wird beim Klick auf einen Punkt gesendet und liefert dessen Index

SwipeToClose

Tag: <SwipeToClose> — umschließt seinen Default-Slot mit einem Container, der auf Touch reagiert und sich wegwischen lässt.

Props

SwipeToCloseProps

PropTypStandardBeschreibung
direction'up' | 'down'PflichtWischrichtung, die das Schließen auslöst. Nimm "up", um eine Lightbox zu schließen, und "down" für Stories
enabledbooleantrueOb die Geste zum Wegwischen aktiv ist
thresholdnumber0.2Anteil der Viewport-Höhe, der zum Schließen nötig ist (0-1)

Events

EventDatenBeschreibung
close()Wird gesendet, wenn die Wischgeste die Schwelle überschreitet und die Animation zum Schließen fertig ist

Slots

SlotBeschreibung
defaultInhalt, der mit der Geste zum Wegwischen umschlossen wird

Observe

Tag: <Observe :signals="[…]"> — rendert seinen Default-Slot neu, sobald sich eines der Signale ändert, und sonst nichts darum herum.

Das Lesen eines Core-Signals in einer Render-Funktion oder einem Template abonniert es nicht. toVueRef ist die übliche Antwort, bindet das Signal aber an die Komponente, die es aufruft; ein Signal, das sich oft ändert, rendert also die ganze Komponente neu. Observe ist die engere Grenze: Lege es um den Teil, der den Wert zeigt, und der Rest der Komponente bleibt, wie er ist. So zeichnet der Stories Player seinen Header und seinen Fortschrittsbalken neu, ohne einen der beiden Slider neu zu zeichnen.

PropTypBeschreibung
signalsreadonly Subscribable[]Die Signale, denen gefolgt wird. Eine andere Menge tauscht das Abonnement aus, dieselbe Menge behält es. Beim Unmount wird alles freigegeben.

Der Typ der Props wird als ObserveProps exportiert.

vue
<script setup lang="ts">
import { Observe, createTimerController, toVueRef } from '@reelkit/vue';

const timer = createTimerController({ duration: 5000 });

// toVueRef binds the signal to THIS component: every tick re-renders all of
// it. Fine for a small component, wasteful when something heavy sits beside
// the part that changes.
const progress = toVueRef(timer.progress);
</script>

<template>
  <section>
    <!-- Only the slot below re-renders on a tick; the list keeps its DOM. -->
    <Observe :signals="[timer.progress]">
      <progress :value="timer.progress.value" max="1" />
    </Observe>

    <ExpensiveList :items="items" />

    <!-- The same value through toVueRef, for comparison. -->
    <p>{{ Math.round(progress * 100) }}%</p>
  </section>
</template>

Greif zu toVueRef, wenn die ganze Komponente dem Signal folgt, und zu Observe, wenn das nur ein Bereich davon tut.

SoundProvider

Tag: <SoundProvider> — ein Kontext-Provider, der eine Instanz von SoundController erzeugt und sie über RK_SOUND_KEY an die Nachfahren weitergibt. Rendert seinen Default-Slot unverändert.

vue
<template>
  <SoundProvider>
    <Reel :count="items.length">
      <template #item="{ index }">
        <VideoSlide :index="index" />
      </template>
      <MuteButton />
    </Reel>
  </SoundProvider>
</template>

RK_REEL_KEY & useReelContext

Ein InjectionKey<ReelContextValue>, den <Reel> seinen Nachfahren bereitstellt. <ReelIndicator> nutzt ihn intern, um sich selbst zu verbinden. Rufe useReelContext() in eigenen Komponenten auf, die den Kontext des Sliders brauchen.

vue
<script setup lang="ts">
import { useReelContext } from '@reelkit/vue';

const ctx = useReelContext();

function jump(index: number) {
  ctx?.goTo(index, true);
}
</script>
EigenschaftTypBeschreibung
indexSignal<number>Reaktiver Index des aktuellen Slides
countSignal<number>Reaktive Gesamtzahl der Elemente
goTo(index: number, animate?: boolean) => Promise<void>Navigiert im Code zu einem Slide

Composables

useBodyLock

Sperrt das Scrollen des Dokument-Body, solange der übergebene Wert true ist. Zählt die Referenzen mit, sodass mehrere gleichzeitige Aufrufer unabhängig voneinander sperren und entsperren können. Entsperrt beim Unmount automatisch.

typescript
import { ref } from 'vue';
import { useBodyLock } from '@reelkit/vue';

const isOpen = ref(false);
useBodyLock(isOpen);

// Also accepts a static boolean
useBodyLock(true);
ParameterTypBeschreibung
lockedRef<boolean> | booleanOb das Scrollen des Body gesperrt sein soll. Nimmt ein reaktives Ref oder einen festen Boolean

useFullscreen

UseFullscreenOptions → UseFullscreenReturn

Ein Composable für die Fullscreen API mit Unterstützung für alle Browser. Verlässt den Vollbildmodus beim Unmount automatisch.

typescript
import { ref } from 'vue';
import { useFullscreen } from '@reelkit/vue';

const containerRef = ref<HTMLElement | null>(null);
const { isFullscreen, request, exit, toggle } = useFullscreen({
  elementRef: containerRef,
});
RückgabeTypBeschreibung
isFullscreenSignal<boolean>Core-Signal mit dem aktuellen Vollbildzustand (lies .value)
request() => Promise<void>Fordert den Vollbildmodus für das referenzierte Element an. Ist schon ein anderes Element im Vollbild, wird dieses zuerst verlassen (mit await).
exit() => Promise<void>Verlässt den Vollbildmodus
toggle() => Promise<void>Schaltet den Vollbildmodus um

useSoundState

Liefert den aktuellen SoundController aus dem Kontext. Muss innerhalb eines <SoundProvider> aufgerufen werden, außerhalb wirft es einen Fehler.

typescript
import { useSoundState } from '@reelkit/vue';

// Inside a SoundProvider descendant
const sound = useSoundState();

sound.muted;    // Signal<boolean>
sound.toggle(); // Toggle muted state

useOverlayUrlState

OverlayUrlStateOptions

Baut einen URL-State-Controller für ein Overlay, den du einem <LightboxUrlOverlay> als Prop :controller übergibst.

Den Ablauf und Beispiele findest du unter URL-State im Vue-Leitfaden.

OptionTypStandardBeschreibung
paramstringPflichtQuery-Parameter, der den aktiven Slide trägt, zum Beispiel "photo".
adapterUrlAdapterHistory APIDas Navigationssystem, über das gelesen und geschrieben wird. Übergib in einer App mit Router einen Adapter auf Basis des Routers, damit dessen eigene Location nicht veraltet.
codec{ decode(raw) => Id | null; encode(id) => string }PflichtFormat auf der Leitung: Parametertext ↔ eine stabile Identität, ohne Wissen über die Sammlung. Bildet mit locator ein zusammengehöriges Paar mit demselben Id — verteile ...urlIndexKey(() => props.images.length) für die Standardgalerie mit Index wie ?photo=3, oder liefere ein eigenes (base64, Slug), damit ein Lesezeichen eine neu sortierte Galerie übersteht.
locator{ locate(id) => number | null; locateAsync?(id) => Promise<number | null>; identify(index) => id }PflichtBildet die Identität auf eine Position ab und ist selbst für deren Gültigkeit zuständig: locate (synchron), locateAsync (asynchroner Rückfall für eine seitenweise geladene Galerie), identify (Schreiben). Für eine einfache Galerie mit Index verteilst du ...urlIndexKey(() => props.images.length) — das liefert diesen Locator samt passendem Codec und prüft ?photo=3 gegen die aktuelle Anzahl, sodass ein veraltetes ?photo=99 aus der URL verschwindet, statt einen Slide zu öffnen, den nie jemand gemeint hat. Übergib einen Getter, keine Zahl, denn ein setup in Vue läuft nur einmal, und eine dort festgehaltene Länge veraltet, sobald ein seitenweise geladener Feed wächst. Ein seitenweise geladener Feed oder eine Galerie mit Schlüsseln nach Identität liefert stattdessen ein eigenes Paar aus codec + locator.

useVueRouterUrlAdapter

Ein UrlAdapter auf Basis von Vue Router. Übergib ihn in einer App mit Router als Option adapter an useOverlayUrlState, damit der Router die einzige Quelle der Wahrheit für die Navigation bleibt — wer history.pushState am Router vorbei schreibt, lässt dessen Location veralten, und die nächste Navigation verliert den Parameter. Geschrieben wird nur in die Query, also bleiben Pfad, Hash und wiederholte Schlüssel wie ?tag=a&tag=b unberührt. Jede Änderung meldet, ob der Router auf derselben Seite einen Eintrag angelegt, ersetzt oder im Verlauf geblättert hat, sodass eine aus einem <router-link> geöffnete Galerie mit einem Schritt zurück schließt.

Er kommt aus einem eigenen Unterpfad, sodass eine App ohne Router vue-router nie in ihr Bundle zieht. vue-router ist eine optionale Peer-Abhängigkeit ab Version 4.1: Der Adapter trägt seinen Besitzstempel über die Navigationsoption state des Routers, die ältere Versionen ignorieren. Mit einem älteren Router bricht nichts — das Schließen entfernt den Parameter dann nur an Ort und Stelle, statt einen Schritt zurückzugehen.

typescript
import { useVueRouterUrlAdapter } from '@reelkit/vue/vue-router-url-adapter';

const adapter = useVueRouterUrlAdapter();
const photo = useOverlayUrlState({
  param: 'photo',
  adapter,
  ...urlIndexKey(() => props.images.length),
});

toVueRef

Verbindet ein Subscribable aus dem Core (jedes Signal aus @reelkit/core) mit einem schreibgeschützten Ref von Vue. Nutze es immer dann, wenn der Wert eines Core-Signals ein Re-Render in Vue auslösen soll — ein direktes Lesen von signal.value in Render-Funktionen oder Templates ist für sich allein nicht reaktiv.

Das Abonnement wird über onScopeDispose automatisch aufgeräumt, darum muss es innerhalb von setup() in Vue oder einem anderen Kontext mit Effect-Scope aufgerufen werden.

typescript
import { defineComponent, h } from 'vue';
import { toVueRef, useSoundState } from '@reelkit/vue';

export const MuteIcon = defineComponent({
  setup() {
    const sound = useSoundState();
    const muted = toVueRef(sound.muted); // Readonly<Ref<boolean>>

    return () => h('span', muted.value ? '🔇' : '🔊');
  },
});

asGenericPropKeys

Baut eine Komponente, die generisch über die Elemente ist, die sie bekommt, so wie ReelPlayerOverlay und StoriesOverlay. Vue 3.3 akzeptiert eine Setup-Funktion mit Typparameter, defineComponent(<T>(props: Props<T>) => …, options), behält diesen Parameter aber nur, wenn options.props Namen von Props auflistet. Übergibst du ein Props-Objekt, liest Vue die Typen der Props aus dem Objekt zurück, und T geht verloren. asGenericPropKeys reicht das Props-Objekt unverändert durch, während der Typchecker eine Liste von Namen sieht: T wird aus dem abgeleitet, was die Elternkomponente bindet, und Standardwerte, Validierung und die Umwandlung in Boolean greifen zur Laufzeit weiterhin.

Das Typargument ist der Props-Typ mit dem Basiselement, und die Komponente braucht Vue 3.3 oder neuer. Slots und Emits können T nicht nutzen: Typisiere einen Slot-Scope, indem du ihn im Template annotierst.

typescript
import { defineComponent, h, type PropType } from 'vue';
import { asGenericPropKeys } from '@reelkit/vue';

interface Item {
  id: string;
}

interface FeedProps<T extends Item> {
  items: T[];
  loop?: boolean;
}

const feedProps = {
  items: { type: Array as PropType<Item[]>, required: true as const },
  loop: { type: Boolean, default: false },
};

export const Feed = defineComponent(
  <T extends Item = Item>(props: FeedProps<T>) =>
    () =>
      h('ul', props.items.map((item) => h('li', item.id))),
  { name: 'Feed', props: asGenericPropKeys<FeedProps<Item>>(feedProps) },
);

hasRenderedNodes

hasRenderedNodes(nodes) sagt, ob ein Slot-Ergebnis etwas auf die Seite bringt, für eine Komponente, die selbst rendert, wenn der Elternteil einen Slot leer lässt, so wie die Overlays. Ein Slot, dessen Inhalt hinter einem falschen v-if steht, gibt trotzdem einen Kommentarknoten zurück, sodass eine Prüfung der Arraylänge ihn als gefüllt werten und den Fallback verwerfen würde. hasRenderedNodes entpackt Fragmente, ignoriert Kommentarknoten und gibt für null oder undefined false zurück, also ist hasRenderedNodes(slots.controls?.(scope)) die Prüfung, an der du verzweigst.

Barrierefreiheit

<Reel> wird als role="region" mit aria-roledescription="carousel" gerendert. Übergib aria-label (in TS heißt das Prop ariaLabel), um der Region einen Namen für Screenreader zu geben. Eine höfliche Live-Region kündigt bei jedem Wechsel "Slide N of M" an. Inaktive Slides bekommen das Attribut inert, damit Fokus und Navigation mit Hilfstechnik sie überspringen.

<ReelIndicator> wird als role="tablist" gerendert, mit wanderndem Tabindex auf den Punkten; die Pfeiltasten bewegen den Fokus, und Enter oder Leertaste aktivieren den Slide.

Baust du ein eigenes Modal um <Reel>? captureFocusForReturn, createFocusTrap und getFocusableElements werden aus @reelkit/vue erneut exportiert, für die Rückgabe des Fokus und die Fokusfalle.

Exporte des Pakets

Alle öffentlichen Exporte von @reelkit/vue:

typescript
// Components
import {
  Reel,
  ReelIndicator,
  SwipeToClose,
  SoundProvider,
  Observe,
} from '@reelkit/vue';

// Types
import type {
  ReelExpose,
  ReelContextValue,
  SwipeToCloseDirection,
  SwipeToCloseProps,
  UseFullscreenOptions,
  UseFullscreenReturn,
} from '@reelkit/vue';

// Context & composables
import {
  RK_REEL_KEY,
  useReelContext,
  RK_SOUND_KEY,
  useBodyLock,
  useFullscreen,
  useSoundState,
  toVueRef,
} from '@reelkit/vue';

// Generic components
import { asGenericPropKeys } from '@reelkit/vue';

// Utilities (re-exported from @reelkit/core)
import {
  createDefaultKeyExtractorForLoop,
  defaultRangeExtractor,
} from '@reelkit/vue';

// Core re-exports
import {
  // Signals & reactivity
  createSignal, createComputed, reaction, batch, createDeferred,

  // Transitions
  slideTransition, fadeTransition, flipTransition,
  cubeTransition, zoomTransition, getSlideProgress,

  // Content loading & preloading
  createContentLoadingController, createContentPreloader,
  observeMediaLoading,

  // Sound
  createSoundController, syncMutedToVideo,

  // Fullscreen
  fullscreenSignal, requestFullscreen, exitFullscreen,

  // DOM & cleanup
  observeDomEvent, createDisposableList, createBodyLock, sharedBodyLock,

  // Size detection
  observeElementSize, getViewportSize, observeViewportSize,
  isMobileWidth, fitAspectRatio, isTouchDevice,

  // Autoplay & download
  createTimerController, createSlideshowController, downloadFile,

  // Focus management
  captureFocusForReturn, createFocusTrap, getFocusableElements,

  // Gestures
  createGestureController,

  // Video
  captureFrame, createSharedVideo,

  // Animation
  animate,

  // Utilities
  noop, clamp, abs, first, last, extractRange,
  lerp, isNegative, generate,
} from '@reelkit/vue';