Referência da API para Vue

Referência completa dos componentes, dos composables e dos utilitários de @reelkit/vue.

Reel

Tag: <Reel>

Props

ReelProps

PropTipoPadrãoDescrição
countnumberobrigatórioNúmero total de slides
direction'vertical' | 'horizontal''vertical'Direção do deslocamento
size[number, number] | undefinedundefinedLargura e altura, na forma [largura, altura]. Sem esta prop, o tamanho é medido pelo ResizeObserver
initialIndexnumber0Índice do slide inicial
loopbooleanfalseLiga o ciclo infinito
transitionTransitionTransformFnslideTransitionFunção do efeito de transição. Prontas: slideTransition, fadeTransition, flipTransition, cubeTransition, zoomTransition
transitionDurationnumber300Duração da animação em ms
swipeDistanceFactornumber0.12Limiar do deslize (0-1)
enableGesturesbooleantrueLiga a navegação por arrasto de toque ou mouse
enableNavKeysbooleantrueLiga a navegação pelas setas do teclado
enableWheelbooleanfalseLiga a navegação pela roda do mouse
wheelDebounceMsnumber200Debounce do evento de roda, em ms
rangeExtractor(index: number, count: number) => number[]defaultRangeExtractorFunção própria para decidir quais índices são renderizados
keyExtractor(index: number, indexInRange: number) => stringindex => index.toString()Função de chave própria para a renderização dos slides (útil com loop)
ariaLabelstringundefinedRótulo acessível da região do carrossel
reelStyleRecord<string, string | number>undefinedEstilos inline aplicados ao elemento contêiner raiz
reelClassstring | Array | ObjectundefinedClasse ou classes CSS aplicadas ao elemento contêiner raiz
onNavKeyPress(increment: -1 | 1) => voidundefinedProp de callback que substitui a navegação padrão por ArrowUp/ArrowDown. Ao informá-la, a navegação passa a ser sua (chame reelRef.value.next(), por exemplo). Omita para manter o comportamento padrão.

Eventos

EventoCargaDescrição
beforeChange(index: number, nextIndex: number, indexInRange: number)Emitido antes de a transição de slide começar
afterChange(index: number, indexInRange: number)Emitido depois que a transição de slide termina
slideDragStart(index: number)Emitido quando um gesto de arrasto começa
slideDragEnd(index: number)Emitido quando um gesto de arrasto termina (dedo solto)
slideDragCanceled(index: number)Emitido quando um gesto de arrasto é cancelado (o slide volta ao lugar)
tap(event: GestureCommonEvent)Emitido em um gesto de toque único
doubleTap(event: GestureCommonEvent)Emitido em um gesto de toque duplo
longPress(event: GestureCommonEvent)Emitido quando um gesto de toque longo começa
longPressEnd(event: GestureEvent)Emitido quando um gesto de toque longo termina

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>
SlotProps com escopoDescrição
#item{ index: number, indexInRange: number, size: [number, number] }Desenha cada slide visível. Chamado para cada índice do intervalo virtualizado
defaultnenhumaConteúdo sobreposto a todos os slides (indicadores, controles e afins)

ReelExpose

API imperativa exposta por um ref de template:

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>
MétodoTipoDescrição
next()() => voidVai para o próximo slide
prev()() => voidVai para o slide anterior
goTo(index, animate?)(number, boolean?) => Promise<void>Navega até um índice de slide específico
adjust()() => voidRecalcula as posições dos slides (útil depois de uma mudança de layout)
observe()() => voidComeça a escutar os eventos de gesto, teclado e roda
unobserve()() => voidPara de escutar os eventos de gesto, teclado e roda

ReelIndicator

Tag: <ReelIndicator>

Props

ReelIndicatorProps

PropTipoPadrãoDescrição
countnumber | undefinedautomáticoNúmero total de itens. Vem sozinho do contexto do Reel que o envolve; passe explicitamente ao usar o indicador isolado
activenumber | undefinedautomáticoÍndice ativo no momento. Vem sozinho do contexto do Reel que o envolve; passe explicitamente ao usar o indicador isolado
direction'vertical' | 'horizontal''vertical'Orientação do indicador
radiusnumber3Raio do ponto em pixels
visiblenumber5Máximo de pontos em tamanho normal visíveis ao mesmo tempo
gapnumber4Espaço entre os pontos em pixels
activeColorstring'#fff'Cor do ponto ativo
inactiveColorstring'rgba(255, 255, 255, 0.5)'Cor dos pontos inativos
edgeScalenumber0.5Fator de escala dos pontos que transbordam nas bordas
onDotClick(index: number) => voidundefinedTratador de clique próprio. Dentro de um Reel, sem esta prop, o clique navega até o ponto escolhido
indicatorClassstring | Array | ObjectundefinedClasse ou classes CSS aplicadas ao elemento raiz do tablist
indicatorStyleCSSPropertiesundefinedEstilos inline mesclados ao elemento raiz do tablist

Eventos

EventoCargaDescrição
dotClick(index: number)Emitido quando um ponto é clicado; entrega o índice do ponto

SwipeToClose

Tag: <SwipeToClose> — envolve o slot padrão em um contêiner sensível ao toque, que dá para dispensar com um deslize.

Props

SwipeToCloseProps

PropTipoPadrãoDescrição
direction'up' | 'down'obrigatórioDireção do deslize que fecha. Use "up" para dispensar a galeria, "down" para dispensar os stories
enabledbooleantrueSe o gesto de deslizar para fechar está ativo
thresholdnumber0.2Fração da altura da tela necessária para fechar (0-1)

Eventos

EventoCargaDescrição
close()Emitido quando o deslize passa do limiar e a animação de fechamento termina

Slots

SlotDescrição
defaultConteúdo a envolver com o gesto de deslizar para fechar

Observe

Tag: <Observe :signals="[…]"> — re-renderiza o seu slot padrão sempre que um dos sinais muda, e nada ao redor.

Ler um sinal do core numa função de render ou num template não inscreve você nele. A resposta de sempre é toVueRef, mas ele amarra o sinal ao componente que o chamou, então um sinal que muda com frequência re-renderiza o componente inteiro. Observe é a fronteira mais estreita: coloque-o em volta da parte que mostra o valor e o resto do componente fica parado. É assim que o player de stories repinta o cabeçalho e a barra de progresso sem redesenhar nenhum dos sliders.

PropTipoDescrição
signalsreadonly Subscribable[]Sinais a acompanhar. Um conjunto diferente troca a inscrição; o mesmo conjunto a mantém. Tudo é liberado na desmontagem.

O tipo das props é exportado como ObserveProps.

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>

Use toVueRef quando o componente inteiro acompanha o sinal, e Observe quando só um pedaço dele acompanha.

SoundProvider

Tag: <SoundProvider> — provedor de contexto que cria uma instância de SoundController e a entrega aos descendentes por RK_SOUND_KEY. Renderiza o slot padrão de forma transparente.

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

RK_REEL_KEY e useReelContext

Uma InjectionKey<ReelContextValue> que o <Reel> fornece aos seus descendentes. Usada internamente pelo <ReelIndicator> para se ligar sozinho. Use useReelContext() em componentes seus que precisem do contexto do slider.

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

const ctx = useReelContext();

function jump(index: number) {
  ctx?.goTo(index, true);
}
</script>
PropriedadeTipoDescrição
indexSignal<number>Índice reativo do slide atual
countSignal<number>Contagem reativa do total de itens
goTo(index: number, animate?: boolean) => Promise<void>Navega até um slide por código

Composables

useBodyLock

Trava a rolagem do corpo do documento enquanto o valor informado for true. Usa contagem de referências, então vários chamadores simultâneos travam e destravam de forma independente. Destrava sozinho na desmontagem.

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

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

// Also accepts a static boolean
useBodyLock(true);
ParâmetroTipoDescrição
lockedRef<boolean> | booleanSe a rolagem do corpo deve ficar travada. Aceita um ref reativo ou um booleano fixo

useFullscreen

UseFullscreenOptions → UseFullscreenReturn

Composable para lidar com a Fullscreen API com suporte entre navegadores. Sai da tela cheia sozinho na desmontagem.

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

const containerRef = ref<HTMLElement | null>(null);
const { isFullscreen, request, exit, toggle } = useFullscreen({
  elementRef: containerRef,
});
RetornoTipoDescrição
isFullscreenSignal<boolean>Sinal do core que reflete o estado atual de tela cheia (leia .value)
request() => Promise<void>Pede tela cheia no elemento referenciado. Se outro elemento já estiver em tela cheia, ele sai primeiro (com espera).
exit() => Promise<void>Sai da tela cheia
toggle() => Promise<void>Alterna o estado de tela cheia

useSoundState

Alcança o SoundController atual pelo contexto. Precisa ser chamado dentro de um <SoundProvider>. Lança erro se for chamado fora.

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

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

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

useOverlayUrlState

OverlayUrlStateOptions

Monta um controlador de estado na URL para o overlay, que você entrega a um <LightboxUrlOverlay> pela prop :controller.

Veja Estado na URL no guia para Vue para o passo a passo e os exemplos.

OpçãoTipoPadrãoDescrição
paramstringobrigatórioParâmetro de consulta que carrega o slide ativo, por exemplo "photo".
adapterUrlAdapterHistory APISistema de navegação por onde ler e escrever. Em um app com rotas, passe um adaptador ligado ao roteador para que a localização dele não fique desatualizada.
codec{ decode(raw) => Id | null; encode(id) => string }obrigatórioFormato do fio: texto do parâmetro ↔ identidade estável, sem enxergar a coleção. Anda junto do locator como par combinado que divide o mesmo Id — espalhe ...urlIndexKey(() => props.images.length) para a galeria padrão endereçada por índice em ?photo=3, ou forneça o seu (base64, slug) para que um favorito sobreviva à galeria mudar de ordem.
locator{ locate(id) => number | null; locateAsync?(id) => Promise<number | null>; identify(index) => id }obrigatórioLiga a identidade a uma posição e responde pela própria validade: locate (síncrono), locateAsync (plano B assíncrono de uma galeria paginada) e identify (nas escritas). Em uma galeria simples por índice, espalhe ...urlIndexKey(() => props.images.length) — ele fornece este locator mais o codec correspondente e limita ?photo=3 pela contagem atual, então um ?photo=99 velho se cura saindo da URL em vez de abrir um slide que ninguém nomeou. Passe um getter, e não um número, porque o setup do Vue roda uma única vez e um comprimento capturado envelheceria conforme o feed paginado cresce. Um feed paginado ou uma galeria endereçada por identidade fornece o próprio par de codec e locator.

useVueRouterUrlAdapter

Um UrlAdapter apoiado no Vue Router. Passe-o na opção adapter de useOverlayUrlState em um app com rotas, para que o roteador siga sendo a única fonte de verdade da navegação — escrever history.pushState por trás dele deixa sua localização desatualizada, e a navegação seguinte derruba o parâmetro. As escritas mexem apenas na consulta, então o caminho, o hash e chaves repetidas como ?tag=a&tag=b seguem intactos. Cada mudança informa se o roteador empilhou na mesma página, substituiu ou andou pelo histórico, de modo que uma galeria aberta por um <router-link> fecha com um único passo para trás.

Vem de um subcaminho próprio, então um app sem roteador nunca puxa vue-router para o bundle. vue-router é uma peer dependency opcional, da versão 4.1 em diante: o adaptador leva seu carimbo de propriedade pela opção de navegação state do roteador, que as versões mais antigas ignoram. Em um roteador antigo nada quebra — fechar apenas limpa o parâmetro no lugar, em vez de dar um passo para trás.

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

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

toVueRef

Faz a ponte de um Subscribable do core (qualquer Signal de @reelkit/core) para um Ref somente leitura do Vue. Use sempre que o valor de um sinal do core precisar provocar uma nova renderização no Vue — ler signal.value direto em funções de renderização ou em templates não é reativo por si só.

A assinatura é descartada sozinha por onScopeDispose, então isto precisa ser chamado dentro de um setup() do Vue ou de outro contexto com escopo de efeito.

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

Cria um componente genérico em relação aos itens que recebe, como ReelPlayerOverlay e StoriesOverlay. O Vue 3.3 aceita uma função setup com parâmetro de tipo, defineComponent(<T>(props: Props<T>) => …, options), mas só mantém esse parâmetro quando options.props lista nomes de props. Passe um objeto de props e o Vue lê os tipos das props de volta a partir do objeto, então T se perde. asGenericPropKeys repassa o objeto de props intacto enquanto o verificador de tipos vê uma lista de nomes: T é inferido do que o componente pai vincula, e os valores padrão, a validação e a conversão de booleanos continuam valendo em tempo de execução.

O argumento de tipo é o tipo das props com o item base, e o componente precisa do Vue 3.3 ou mais recente. Slots e eventos não podem usar T: para tipar o escopo de um slot, anote-o no template.

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) diz se o resultado de um slot vai colocar algo na página, para um componente que volta à própria renderização quando o pai deixa um slot vazio, como fazem os overlays. Um slot cujo conteúdo está atrás de um v-if falso ainda devolve um nó de comentário, então checar o tamanho do array o trataria como preenchido e descartaria o fallback. hasRenderedNodes desembrulha fragmentos, ignora nós de comentário e devolve false para null ou undefined, então hasRenderedNodes(slots.controls?.(scope)) é o teste em que se basear.

Acessibilidade

<Reel> é renderizado como role="region" com aria-roledescription="carousel". Passe aria-label (no TypeScript a prop é ariaLabel) para dar à região um nome que o leitor de tela anuncie. Uma live region educada anuncia "Slide N de M" a cada troca. Os slides inativos recebem o atributo inert, então o foco e a navegação por tecnologia assistiva passam por cima deles.

<ReelIndicator> é renderizado como role="tablist", com tabindex móvel nos pontos; as setas movem o foco e Enter ou Espaço ativa o slide.

Montando um modal próprio em volta de <Reel>? captureFocusForReturn, createFocusTrap e getFocusableElements são reexportados por @reelkit/vue para devolver e prender o foco.

Exports do pacote

Todos os exports públicos de @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';