docs(vue): add interactive demo for every composable

A beautiful, SSR-safe demo.vue next to each composable, auto-discovered by the docs extractor and rendered client-only on each composable's page.
This commit is contained in:
2026-06-08 15:51:16 +07:00
parent 59e995d0b5
commit e83f10fe32
214 changed files with 19584 additions and 74 deletions
@@ -0,0 +1,91 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useThrottleFn } from './index';
const delay = ref(400);
const moves = ref(0);
const fires = ref(0);
const position = ref({ x: 0, y: 0 });
const onMove = useThrottleFn((x: number, y: number) => {
fires.value++;
position.value = { x, y };
}, delay, true, true);
function handlePointer(event: PointerEvent) {
moves.value++;
const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();
const x = Math.round(((event.clientX - rect.left) / rect.width) * 100);
const y = Math.round(((event.clientY - rect.top) / rect.height) * 100);
onMove(Math.max(0, Math.min(100, x)), Math.max(0, Math.min(100, y)));
}
const ratio = () => (moves.value ? Math.round((fires.value / moves.value) * 100) : 0);
</script>
<template>
<div class="flex w-full max-w-md flex-col gap-4">
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">useThrottleFn</span>
<div
class="relative h-40 w-full overflow-hidden rounded-xl border border-(--border) bg-(--bg-inset) touch-none"
@pointermove="handlePointer"
>
<span
class="pointer-events-none absolute size-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-(--accent-fg) bg-(--accent) shadow-md transition-all duration-150 ease-out"
:style="{ left: `${position.x}%`, top: `${position.y}%` }"
/>
<span class="pointer-events-none absolute inset-x-0 bottom-2 text-center text-xs text-(--fg-subtle)">
Move your pointer over this area
</span>
</div>
<div class="flex items-center gap-3">
<label class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)" for="window">
Window
</label>
<input
id="window"
v-model.number="delay"
type="range"
min="0"
max="1000"
step="50"
class="flex-1 accent-(--accent)"
>
<span class="w-16 text-right font-mono text-sm tabular-nums text-(--fg)">{{ delay }}ms</span>
</div>
<div class="grid grid-cols-3 gap-3">
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3">
<p class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">Events</p>
<p class="mt-1 font-mono text-2xl font-bold tabular-nums text-(--fg)">{{ moves }}</p>
</div>
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3">
<p class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">Fired</p>
<p class="mt-1 font-mono text-2xl font-bold tabular-nums text-(--accent-text)">{{ fires }}</p>
</div>
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3">
<p class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">Ran</p>
<p class="mt-1 font-mono text-2xl font-bold tabular-nums text-(--fg)">{{ ratio() }}%</p>
</div>
</div>
<div class="flex items-center justify-between gap-2">
<span class="rounded-md border border-(--border) bg-(--bg-inset) px-2 py-0.5 font-mono text-xs text-(--fg-muted)">
x: {{ position.x }} · y: {{ position.y }}
</span>
<button
type="button"
class="inline-flex items-center justify-center gap-1.5 rounded-lg border border-(--border) bg-(--bg-elevated) px-3 py-1.5 text-sm font-medium text-(--fg) transition hover:border-(--border-strong) hover:bg-(--bg-inset) active:scale-[0.98] cursor-pointer"
@click="onMove.flush()"
>
Flush trailing
</button>
</div>
<p class="text-xs text-(--fg-subtle)">
Leading + trailing throttling caps the handler to once per window drag faster and watch the fired count lag behind events.
</p>
</div>
</template>