refactor(toolkit): type source any with proper types
Genuinely type composable any usages (useStepper/useStorage/useForm/ createEventHook/useSorted/etc.) as proper generics/unknown; keep idiomatic any-function and overload-impl signatures with comments; skipped test -> .todo.
This commit is contained in:
@@ -27,51 +27,51 @@ function reset() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-sm flex flex-col gap-4">
|
||||
<div class="demo-stack max-w-sm">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">DOM removal watcher</span>
|
||||
<span class="demo-label">DOM removal watcher</span>
|
||||
<span
|
||||
class="inline-flex items-center gap-1.5 rounded-md border border-(--border) bg-(--bg-inset) px-2 py-0.5 text-xs font-medium text-(--fg-muted)"
|
||||
class="demo-badge"
|
||||
>
|
||||
<span class="size-1.5 rounded-full transition" :class="mounted ? 'bg-emerald-500' : 'bg-(--fg-subtle)'" />
|
||||
<span class="size-1.5 rounded-full transition" :class="mounted ? 'bg-emerald-500' : 'bg-fg-subtle'" />
|
||||
{{ mounted ? 'In DOM' : 'Removed' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-(--border) bg-(--bg-elevated) p-4 min-h-28 flex items-center justify-center">
|
||||
<div class="demo-card p-4 min-h-28 flex items-center justify-center">
|
||||
<div
|
||||
v-if="mounted"
|
||||
ref="watched"
|
||||
class="flex w-full flex-col items-center gap-1 rounded-lg border border-(--accent) bg-(--accent-subtle) px-4 py-6 text-center transition"
|
||||
class="flex w-full flex-col items-center gap-1 rounded-lg border border-accent bg-accent-subtle px-4 py-6 text-center transition"
|
||||
>
|
||||
<span class="text-sm font-medium text-(--accent-text)">Watched element</span>
|
||||
<span class="text-xs text-(--fg-muted)">Remove me and the callback fires</span>
|
||||
<span class="text-sm font-medium text-accent-text">Watched element</span>
|
||||
<span class="text-xs text-fg-muted">Remove me and the callback fires</span>
|
||||
</div>
|
||||
<span v-else class="text-sm text-(--fg-subtle)">Element detached from the document</span>
|
||||
<span v-else class="text-sm text-fg-subtle">Element detached from the document</span>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 text-center">
|
||||
<div class="font-mono text-3xl font-bold tabular-nums text-(--fg)">{{ removals }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">removals fired</div>
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 text-center">
|
||||
<div class="demo-stat text-3xl">{{ removals }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">removals fired</div>
|
||||
</div>
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 text-center flex flex-col justify-center">
|
||||
<div class="font-mono text-sm font-medium tabular-nums text-(--fg)">{{ lastEvent ?? '—' }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">last fired</div>
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 text-center flex flex-col justify-center">
|
||||
<div class="font-mono text-sm font-medium tabular-nums text-fg">{{ lastEvent ?? '—' }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">last fired</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex flex-1 items-center justify-center gap-1.5 rounded-lg border border-transparent bg-(--accent) px-3 py-1.5 text-sm font-medium text-(--accent-fg) transition hover:bg-(--accent-hover) active:scale-[0.98] cursor-pointer"
|
||||
class="demo-btn-primary flex-1"
|
||||
@click="toggle"
|
||||
>
|
||||
{{ mounted ? 'Remove element' : 'Mount element' }}
|
||||
</button>
|
||||
<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:bg-(--bg-inset) hover:border-(--border-strong) active:scale-[0.98] cursor-pointer disabled:cursor-not-allowed disabled:opacity-40 disabled:active:scale-100"
|
||||
class="demo-btn disabled:cursor-not-allowed disabled:opacity-40 disabled:active:scale-100"
|
||||
:disabled="removals === 0"
|
||||
@click="reset"
|
||||
>
|
||||
|
||||
@@ -15,9 +15,9 @@ const activeTag = computed(() => activeElement.value?.tagName.toLowerCase() ?? n
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-sm flex flex-col gap-4">
|
||||
<div class="demo-stack max-w-sm">
|
||||
<div class="space-y-3">
|
||||
<p class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">
|
||||
<p class="demo-label">
|
||||
Focus a field
|
||||
</p>
|
||||
<div
|
||||
@@ -27,39 +27,39 @@ const activeTag = computed(() => activeElement.value?.tagName.toLowerCase() ?? n
|
||||
>
|
||||
<label
|
||||
:for="field.id"
|
||||
class="text-sm font-medium text-(--fg-muted)"
|
||||
class="text-sm font-medium text-fg-muted"
|
||||
>{{ field.label }}</label>
|
||||
<input
|
||||
:id="field.id"
|
||||
:type="field.type"
|
||||
:placeholder="field.placeholder"
|
||||
class="w-full rounded-lg border border-(--border) bg-(--bg) px-3 py-2 text-sm text-(--fg) placeholder:text-(--fg-subtle) transition focus:border-(--accent) focus:outline-none focus:ring-2 focus:ring-(--ring)"
|
||||
class="demo-input"
|
||||
>
|
||||
</div>
|
||||
<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:bg-(--bg-inset) hover:border-(--border-strong) active:scale-[0.98] cursor-pointer"
|
||||
class="demo-btn"
|
||||
>
|
||||
A focusable button
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 font-mono text-sm text-(--fg) tabular-nums">
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 font-mono text-sm text-fg tabular-nums">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<span class="text-(--fg-subtle)">activeElement</span>
|
||||
<span class="text-fg-subtle">activeElement</span>
|
||||
<span
|
||||
v-if="activeTag"
|
||||
class="inline-flex items-center gap-1.5 rounded-md border border-(--border) bg-(--bg-elevated) px-2 py-0.5 text-xs font-medium text-(--accent-text)"
|
||||
class="inline-flex items-center gap-1.5 rounded-md border border-border bg-bg-elevated px-2 py-0.5 text-xs font-medium text-accent-text"
|
||||
>
|
||||
<{{ activeTag }}>
|
||||
</span>
|
||||
<span
|
||||
v-else
|
||||
class="text-xs text-(--fg-subtle)"
|
||||
class="text-xs text-fg-subtle"
|
||||
>none</span>
|
||||
</div>
|
||||
<div class="mt-2 flex items-center justify-between gap-3">
|
||||
<span class="text-(--fg-subtle)">id</span>
|
||||
<span class="text-fg-subtle">id</span>
|
||||
<span class="truncate">{{ activeId ?? '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,9 +14,9 @@ const activeIndex = computed(() => stages.findIndex(s => s.state === readyState.
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-sm flex flex-col gap-4">
|
||||
<div class="demo-stack max-w-sm">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">
|
||||
<span class="demo-label">
|
||||
document.readyState
|
||||
</span>
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border border-emerald-500/30 bg-emerald-500/10 px-2 py-0.5 text-xs font-medium text-emerald-600 dark:text-emerald-400">
|
||||
@@ -31,24 +31,24 @@ const activeIndex = computed(() => stages.findIndex(s => s.state === readyState.
|
||||
:key="stage.state"
|
||||
class="flex items-center gap-3 rounded-lg border p-3 transition"
|
||||
:class="i <= activeIndex
|
||||
? 'border-(--accent) bg-(--accent-subtle)'
|
||||
: 'border-(--border) bg-(--bg-elevated)'"
|
||||
? 'border-accent bg-accent-subtle'
|
||||
: 'border-border bg-bg-elevated'"
|
||||
>
|
||||
<span
|
||||
class="flex size-6 shrink-0 items-center justify-center rounded-full font-mono text-xs font-bold tabular-nums transition"
|
||||
:class="i < activeIndex
|
||||
? 'bg-(--accent) text-(--accent-fg)'
|
||||
? 'bg-accent text-accent-fg'
|
||||
: i === activeIndex
|
||||
? 'bg-(--accent) text-(--accent-fg) ring-4 ring-(--ring)'
|
||||
: 'bg-(--bg-inset) text-(--fg-subtle)'"
|
||||
? 'bg-accent text-accent-fg ring-4 ring-ring'
|
||||
: 'bg-bg-inset text-fg-subtle'"
|
||||
>
|
||||
{{ i + 1 }}
|
||||
</span>
|
||||
<div class="min-w-0">
|
||||
<p class="font-mono text-sm font-medium text-(--fg)">
|
||||
<p class="font-mono text-sm font-medium text-fg">
|
||||
{{ stage.label }}
|
||||
</p>
|
||||
<p class="text-xs text-(--fg-subtle)">
|
||||
<p class="text-xs text-fg-subtle">
|
||||
{{ stage.hint }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -18,8 +18,8 @@ const isVisible = computed(() => visibility.value === 'visible');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-sm flex flex-col gap-4">
|
||||
<div class="rounded-xl border border-(--border) bg-(--bg-elevated) p-4 flex flex-col items-center gap-3 text-center">
|
||||
<div class="demo-stack max-w-sm">
|
||||
<div class="demo-card p-4 flex flex-col items-center gap-3 text-center">
|
||||
<span
|
||||
class="flex size-14 items-center justify-center rounded-full text-2xl transition"
|
||||
:class="isVisible
|
||||
@@ -29,33 +29,33 @@ const isVisible = computed(() => visibility.value === 'visible');
|
||||
{{ isVisible ? '👁️' : '💤' }}
|
||||
</span>
|
||||
<div>
|
||||
<p class="font-mono text-2xl font-bold tabular-nums text-(--fg)">
|
||||
<p class="demo-stat text-2xl">
|
||||
{{ visibility }}
|
||||
</p>
|
||||
<p class="text-xs text-(--fg-subtle)">
|
||||
<p class="text-xs text-fg-subtle">
|
||||
{{ isVisible ? 'This tab is in the foreground' : 'This tab is hidden' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-center text-xs text-(--fg-subtle)">
|
||||
<p class="text-center text-xs text-fg-subtle">
|
||||
Switch to another tab or minimize the window to watch this update.
|
||||
</p>
|
||||
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 text-center">
|
||||
<p class="font-mono text-3xl font-bold tabular-nums text-(--fg)">
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 text-center">
|
||||
<p class="demo-stat text-3xl">
|
||||
{{ switches }}
|
||||
</p>
|
||||
<p class="mt-0.5 text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">
|
||||
<p class="demo-label mt-0.5">
|
||||
Times hidden
|
||||
</p>
|
||||
</div>
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 text-center">
|
||||
<p class="font-mono text-sm font-medium tabular-nums text-(--fg) truncate">
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 text-center">
|
||||
<p class="font-mono text-sm font-medium tabular-nums text-fg truncate">
|
||||
{{ lastHidden ?? '—' }}
|
||||
</p>
|
||||
<p class="mt-0.5 text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">
|
||||
<p class="demo-label mt-0.5">
|
||||
Last hidden at
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -16,20 +16,20 @@ const { x, y, isDragging, style } = useDraggable(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-md flex flex-col gap-4">
|
||||
<div class="demo-stack max-w-md">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">
|
||||
<span class="demo-label">
|
||||
Draggable, clamped to container
|
||||
</span>
|
||||
<span
|
||||
class="inline-flex items-center gap-1.5 rounded-md border px-2 py-0.5 text-xs font-medium transition"
|
||||
:class="isDragging
|
||||
? 'border-(--accent) bg-(--accent-subtle) text-(--accent-text)'
|
||||
: 'border-(--border) bg-(--bg-inset) text-(--fg-muted)'"
|
||||
? 'border-accent bg-accent-subtle text-accent-text'
|
||||
: 'border-border bg-bg-inset text-fg-muted'"
|
||||
>
|
||||
<span
|
||||
class="size-1.5 rounded-full transition"
|
||||
:class="isDragging ? 'bg-(--accent)' : 'bg-(--fg-subtle)'"
|
||||
:class="isDragging ? 'bg-accent' : 'bg-fg-subtle'"
|
||||
/>
|
||||
{{ isDragging ? 'dragging' : 'idle' }}
|
||||
</span>
|
||||
@@ -37,29 +37,29 @@ const { x, y, isDragging, style } = useDraggable(
|
||||
|
||||
<div
|
||||
ref="container"
|
||||
class="relative h-56 w-full overflow-hidden rounded-xl border border-(--border) bg-(--bg-inset)"
|
||||
class="relative h-56 w-full overflow-hidden rounded-xl border border-border bg-bg-inset"
|
||||
>
|
||||
<div
|
||||
ref="card"
|
||||
:style="style"
|
||||
class="absolute w-36 select-none rounded-lg border border-(--border-strong) bg-(--bg-elevated) shadow-lg"
|
||||
:class="isDragging ? 'ring-2 ring-(--ring)' : ''"
|
||||
class="absolute w-36 select-none rounded-lg border border-border-strong bg-bg-elevated shadow-lg"
|
||||
:class="isDragging ? 'ring-2 ring-ring' : ''"
|
||||
>
|
||||
<div
|
||||
ref="handle"
|
||||
class="flex items-center gap-1.5 rounded-t-lg border-b border-(--border) bg-(--bg-subtle) px-3 py-2 cursor-grab active:cursor-grabbing"
|
||||
class="flex items-center gap-1.5 rounded-t-lg border-b border-border bg-bg-subtle px-3 py-2 cursor-grab active:cursor-grabbing"
|
||||
>
|
||||
<span class="text-(--fg-subtle)">⠿</span>
|
||||
<span class="text-xs font-medium text-(--fg-muted)">Drag me</span>
|
||||
<span class="text-fg-subtle">⠿</span>
|
||||
<span class="text-xs font-medium text-fg-muted">Drag me</span>
|
||||
</div>
|
||||
<div class="p-3 font-mono text-xs tabular-nums text-(--fg)">
|
||||
<div class="p-3 font-mono text-xs tabular-nums text-fg">
|
||||
<div>x: {{ Math.round(x) }}</div>
|
||||
<div>y: {{ Math.round(y) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-center text-xs text-(--fg-subtle)">
|
||||
<p class="text-center text-xs text-fg-subtle">
|
||||
Drag from the header. Movement is clamped to the container bounds.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { computed, shallowRef, toValue } from 'vue';
|
||||
import type { ComputedRef, MaybeRefOrGetter, Ref } from 'vue';
|
||||
import { noop } from '@robonen/stdlib';
|
||||
import { clamp, noop } from '@robonen/stdlib';
|
||||
import { defaultWindow } from '@/types';
|
||||
import { useEventListener } from '@/composables/browser/useEventListener';
|
||||
import { unrefElement } from '@/composables/component/unrefElement';
|
||||
@@ -259,13 +259,13 @@ export function useDraggable(
|
||||
if (axis === 'x' || axis === 'both') {
|
||||
x = event.clientX - pressedDelta.value.x;
|
||||
if (container && targetRect)
|
||||
x = Math.min(Math.max(0, x), container.scrollWidth - targetRect.width);
|
||||
x = clamp(x, 0, container.scrollWidth - targetRect.width);
|
||||
}
|
||||
|
||||
if (axis === 'y' || axis === 'both') {
|
||||
y = event.clientY - pressedDelta.value.y;
|
||||
if (container && targetRect)
|
||||
y = Math.min(Math.max(0, y), container.scrollHeight - targetRect.height);
|
||||
y = clamp(y, 0, container.scrollHeight - targetRect.height);
|
||||
}
|
||||
|
||||
position.value = { x, y };
|
||||
|
||||
@@ -24,7 +24,7 @@ function formatSize(bytes: number): string {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-sm flex flex-col gap-4">
|
||||
<div class="demo-stack max-w-sm">
|
||||
<div
|
||||
v-if="!isSupported"
|
||||
class="rounded-xl border border-amber-500/30 bg-amber-500/10 p-4 text-center text-sm text-amber-700 dark:text-amber-300"
|
||||
@@ -37,33 +37,33 @@ function formatSize(bytes: number): string {
|
||||
ref="dropZone"
|
||||
class="flex flex-col items-center justify-center gap-2 rounded-xl border-2 border-dashed p-8 text-center transition"
|
||||
:class="isOverDropZone
|
||||
? 'border-(--accent) bg-(--accent-subtle)'
|
||||
: 'border-(--border-strong) bg-(--bg-elevated)'"
|
||||
? 'border-accent bg-accent-subtle'
|
||||
: 'border-border-strong bg-bg-elevated'"
|
||||
>
|
||||
<span class="text-3xl transition" :class="isOverDropZone ? 'scale-110' : ''">
|
||||
{{ isOverDropZone ? '📥' : '🖼️' }}
|
||||
</span>
|
||||
<p class="text-sm font-medium text-(--fg)">
|
||||
<p class="text-sm font-medium text-fg">
|
||||
{{ isOverDropZone ? 'Release to drop' : 'Drop image files here' }}
|
||||
</p>
|
||||
<p class="text-xs text-(--fg-subtle)">
|
||||
<p class="text-xs text-fg-subtle">
|
||||
Only <span class="font-mono">image/*</span> files are accepted
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3">
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">
|
||||
<span class="demo-label">
|
||||
Dropped files
|
||||
</span>
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border border-(--border) bg-(--bg-elevated) px-2 py-0.5 text-xs font-medium text-(--fg-muted) tabular-nums">
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border border-border bg-bg-elevated px-2 py-0.5 text-xs font-medium text-fg-muted tabular-nums">
|
||||
{{ fileList.length }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p
|
||||
v-if="fileList.length === 0"
|
||||
class="mt-3 text-center text-sm text-(--fg-subtle)"
|
||||
class="mt-3 text-center text-sm text-fg-subtle"
|
||||
>
|
||||
Nothing dropped yet.
|
||||
</p>
|
||||
@@ -72,10 +72,10 @@ function formatSize(bytes: number): string {
|
||||
<li
|
||||
v-for="file in fileList"
|
||||
:key="file.name"
|
||||
class="flex items-center justify-between gap-3 rounded-md bg-(--bg-elevated) px-2.5 py-1.5"
|
||||
class="flex items-center justify-between gap-3 rounded-md bg-bg-elevated px-2.5 py-1.5"
|
||||
>
|
||||
<span class="truncate text-sm text-(--fg)">{{ file.name }}</span>
|
||||
<span class="shrink-0 font-mono text-xs tabular-nums text-(--fg-subtle)">
|
||||
<span class="truncate text-sm text-fg">{{ file.name }}</span>
|
||||
<span class="shrink-0 font-mono text-xs tabular-nums text-fg-subtle">
|
||||
{{ formatSize(file.size) }}
|
||||
</span>
|
||||
</li>
|
||||
|
||||
@@ -34,20 +34,20 @@ function fmt(n: number) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-md flex flex-col gap-4">
|
||||
<div class="rounded-xl border border-(--border) bg-(--bg-elevated) p-4 flex flex-col gap-4">
|
||||
<div class="demo-stack max-w-md">
|
||||
<div class="demo-card p-4 flex flex-col gap-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">getBoundingClientRect</span>
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border border-(--border) bg-(--bg-inset) px-2 py-0.5 text-xs font-medium text-(--fg-muted)">
|
||||
<span class="demo-label">getBoundingClientRect</span>
|
||||
<span class="demo-badge">
|
||||
{{ timing }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- The measured target. Resizing it mutates the reactive bounds. -->
|
||||
<div class="relative h-40 overflow-hidden rounded-lg border border-(--border) bg-(--bg-inset)">
|
||||
<div class="relative h-40 overflow-hidden rounded-lg border border-border bg-bg-inset">
|
||||
<div
|
||||
ref="target"
|
||||
class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 grid place-items-center rounded-lg bg-(--accent) text-(--accent-fg) shadow transition-[width,height] duration-150"
|
||||
class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 grid place-items-center rounded-lg bg-accent text-accent-fg shadow transition-[width,height] duration-150"
|
||||
:style="{ width: `${size}px`, height: `${size}px` }"
|
||||
>
|
||||
<span class="font-mono text-xs font-semibold tabular-nums">{{ fmt(bounds.width.value) }}×{{ fmt(bounds.height.value) }}</span>
|
||||
@@ -58,18 +58,18 @@ function fmt(n: number) {
|
||||
<div
|
||||
v-for="m in metrics"
|
||||
:key="m.label"
|
||||
class="rounded-lg border border-(--border) bg-(--bg-inset) p-2 text-center"
|
||||
class="rounded-lg border border-border bg-bg-inset p-2 text-center"
|
||||
>
|
||||
<div class="font-mono text-sm font-bold tabular-nums text-(--fg)">{{ fmt(m.value) }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">{{ m.label }}</div>
|
||||
<div class="demo-stat text-sm">{{ fmt(m.value) }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">{{ m.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<label class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)" for="bound-size">Size</label>
|
||||
<span class="font-mono text-xs tabular-nums text-(--fg-muted)">{{ size }}px</span>
|
||||
<label class="demo-label" for="bound-size">Size</label>
|
||||
<span class="font-mono text-xs tabular-nums text-fg-muted">{{ size }}px</span>
|
||||
</div>
|
||||
<input
|
||||
id="bound-size"
|
||||
@@ -78,7 +78,7 @@ function fmt(n: number) {
|
||||
min="32"
|
||||
max="140"
|
||||
step="2"
|
||||
class="w-full accent-(--accent) cursor-pointer"
|
||||
class="w-full accent-accent cursor-pointer"
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -89,22 +89,22 @@ function fmt(n: number) {
|
||||
type="button"
|
||||
class="flex-1 inline-flex items-center justify-center gap-1.5 rounded-lg border px-3 py-1.5 text-sm font-medium transition active:scale-[0.98] cursor-pointer"
|
||||
:class="timing === opt
|
||||
? 'border-transparent bg-(--accent) text-(--accent-fg) hover:bg-(--accent-hover)'
|
||||
: 'border-(--border) bg-(--bg-elevated) text-(--fg) hover:bg-(--bg-inset) hover:border-(--border-strong)'"
|
||||
? 'border-transparent bg-accent text-accent-fg hover:bg-accent-hover'
|
||||
: 'border-border bg-bg-elevated text-fg hover:bg-bg-inset hover:border-border-strong'"
|
||||
@click="timing = opt"
|
||||
>
|
||||
{{ opt }}
|
||||
</button>
|
||||
<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:bg-(--bg-inset) hover:border-(--border-strong) active:scale-[0.98] cursor-pointer"
|
||||
class="demo-btn"
|
||||
@click="bounds.update()"
|
||||
>
|
||||
Update
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="text-xs text-(--fg-subtle)">
|
||||
<p class="text-xs text-fg-subtle">
|
||||
<span class="font-mono">next-frame</span> batches rapid scroll/resize reads into one measurement per animation frame.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -25,12 +25,12 @@ function fmt(n: number) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-md flex flex-col gap-4">
|
||||
<div class="rounded-xl border border-(--border) bg-(--bg-elevated) p-4 flex flex-col gap-4">
|
||||
<div class="demo-stack max-w-md">
|
||||
<div class="demo-card p-4 flex flex-col gap-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">ResizeObserver</span>
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border border-(--border) bg-(--bg-inset) px-2 py-0.5 text-xs font-medium text-(--fg-muted)">
|
||||
<span class="size-1.5 rounded-full" :class="observing ? 'bg-emerald-500' : 'bg-(--fg-subtle)'" />
|
||||
<span class="demo-label">ResizeObserver</span>
|
||||
<span class="demo-badge">
|
||||
<span class="size-1.5 rounded-full" :class="observing ? 'bg-emerald-500' : 'bg-fg-subtle'" />
|
||||
{{ observing ? 'Observing' : 'Stopped' }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -40,25 +40,25 @@ function fmt(n: number) {
|
||||
ref="target"
|
||||
readonly
|
||||
:style="{ padding: `${padding}px` }"
|
||||
class="w-full min-h-24 resize rounded-lg border border-(--border-strong) bg-(--bg-inset) text-sm leading-relaxed text-(--fg-muted) focus:outline-none focus:ring-2 focus:ring-(--ring)"
|
||||
class="w-full min-h-24 resize rounded-lg border border-border-strong bg-bg-inset text-sm leading-relaxed text-fg-muted focus:outline-none focus:ring-2 focus:ring-ring"
|
||||
>Drag the bottom-right corner to resize me. The width and height update live as the ResizeObserver fires. Border-box sizing includes the padding below.</textarea>
|
||||
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 text-center">
|
||||
<div class="font-mono text-3xl font-bold tabular-nums text-(--fg)">{{ fmt(width) }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">width px</div>
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 text-center">
|
||||
<div class="demo-stat text-3xl">{{ fmt(width) }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">width px</div>
|
||||
</div>
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 text-center">
|
||||
<div class="font-mono text-3xl font-bold tabular-nums text-(--fg)">{{ fmt(height) }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">height px</div>
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 text-center">
|
||||
<div class="demo-stat text-3xl">{{ fmt(height) }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">height px</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<label class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)" for="size-padding">Padding (border-box)</label>
|
||||
<span class="font-mono text-xs tabular-nums text-(--fg-muted)">{{ padding }}px</span>
|
||||
<label class="demo-label" for="size-padding">Padding (border-box)</label>
|
||||
<span class="font-mono text-xs tabular-nums text-fg-muted">{{ padding }}px</span>
|
||||
</div>
|
||||
<input
|
||||
id="size-padding"
|
||||
@@ -67,20 +67,20 @@ function fmt(n: number) {
|
||||
min="0"
|
||||
max="40"
|
||||
step="2"
|
||||
class="w-full accent-(--accent) cursor-pointer"
|
||||
class="w-full accent-accent cursor-pointer"
|
||||
>
|
||||
</div>
|
||||
|
||||
<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:bg-(--bg-inset) hover:border-(--border-strong) active:scale-[0.98] cursor-pointer disabled:cursor-not-allowed disabled:opacity-40 disabled:active:scale-100"
|
||||
class="demo-btn disabled:cursor-not-allowed disabled:opacity-40 disabled:active:scale-100"
|
||||
:disabled="!observing"
|
||||
@click="toggleObserver"
|
||||
>
|
||||
{{ observing ? 'Stop observing' : 'Observer stopped' }}
|
||||
</button>
|
||||
|
||||
<p class="text-xs text-(--fg-subtle)">
|
||||
<p class="text-xs text-fg-subtle">
|
||||
With <span class="font-mono">box: 'border-box'</span> the reported size includes padding, so the slider changes the numbers without resizing the element.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -22,16 +22,16 @@ watch(isVisible, (visible, was) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-sm flex flex-col gap-4">
|
||||
<div class="demo-stack max-w-sm">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">IntersectionObserver</span>
|
||||
<span class="demo-label">IntersectionObserver</span>
|
||||
<span
|
||||
class="inline-flex items-center gap-1.5 rounded-md border px-2 py-0.5 text-xs font-medium transition"
|
||||
:class="isVisible
|
||||
? 'border-emerald-500/30 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400'
|
||||
: 'border-(--border) bg-(--bg-inset) text-(--fg-muted)'"
|
||||
: 'border-border bg-bg-inset text-fg-muted'"
|
||||
>
|
||||
<span class="size-1.5 rounded-full" :class="isVisible ? 'bg-emerald-500' : 'bg-(--fg-subtle)'" />
|
||||
<span class="size-1.5 rounded-full" :class="isVisible ? 'bg-emerald-500' : 'bg-fg-subtle'" />
|
||||
{{ isVisible ? 'In view' : 'Hidden' }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -39,38 +39,38 @@ watch(isVisible, (visible, was) => {
|
||||
<!-- Scrollable root; the target card sits below the fold until scrolled. -->
|
||||
<div
|
||||
ref="root"
|
||||
class="h-44 overflow-y-auto rounded-xl border border-(--border) bg-(--bg-inset) p-3"
|
||||
class="h-44 overflow-y-auto rounded-xl border border-border bg-bg-inset p-3"
|
||||
>
|
||||
<p class="text-sm text-(--fg-subtle)">Scroll down inside this box…</p>
|
||||
<p class="text-sm text-fg-subtle">Scroll down inside this box…</p>
|
||||
<div class="h-40" />
|
||||
<div
|
||||
ref="target"
|
||||
class="rounded-lg border border-(--border) bg-(--accent) p-4 text-center text-(--accent-fg) shadow transition"
|
||||
class="rounded-lg border border-border bg-accent p-4 text-center text-accent-fg shadow transition"
|
||||
:class="isVisible ? 'opacity-100 scale-100' : 'opacity-60 scale-95'"
|
||||
>
|
||||
<div class="text-sm font-semibold">Target element</div>
|
||||
<div class="text-xs opacity-80">at least 50% visible to count</div>
|
||||
</div>
|
||||
<div class="h-24" />
|
||||
<p class="text-sm text-(--fg-subtle)">…and back up to hide it again.</p>
|
||||
<p class="text-sm text-fg-subtle">…and back up to hide it again.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 text-center">
|
||||
<div class="font-mono text-3xl font-bold tabular-nums text-(--fg)">{{ seenCount }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">times seen</div>
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 text-center">
|
||||
<div class="demo-stat text-3xl">{{ seenCount }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">times seen</div>
|
||||
</div>
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 text-center flex flex-col items-center justify-center">
|
||||
<div class="text-sm font-semibold" :class="isActive ? 'text-emerald-600 dark:text-emerald-400' : 'text-(--fg-muted)'">
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 text-center flex flex-col items-center justify-center">
|
||||
<div class="text-sm font-semibold" :class="isActive ? 'text-emerald-600 dark:text-emerald-400' : 'text-fg-muted'">
|
||||
{{ isActive ? 'Active' : 'Paused' }}
|
||||
</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">observer</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">observer</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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:bg-(--bg-inset) hover:border-(--border-strong) active:scale-[0.98] cursor-pointer"
|
||||
class="demo-btn"
|
||||
@click="isActive ? pause() : resume()"
|
||||
>
|
||||
{{ isActive ? 'Pause observer' : 'Resume observer' }}
|
||||
|
||||
@@ -26,31 +26,31 @@ const fields = [
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-sm flex flex-col gap-4">
|
||||
<div class="demo-stack max-w-sm">
|
||||
<component :is="GuardHost" v-if="enabled" />
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">Focus guards</span>
|
||||
<span class="demo-label">Focus guards</span>
|
||||
<span
|
||||
class="inline-flex items-center gap-1.5 rounded-md border px-2 py-0.5 text-xs font-medium transition"
|
||||
:class="enabled
|
||||
? 'border-emerald-500/30 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400'
|
||||
: 'border-(--border) bg-(--bg-inset) text-(--fg-muted)'"
|
||||
: 'border-border bg-bg-inset text-fg-muted'"
|
||||
>
|
||||
<span class="size-1.5 rounded-full" :class="enabled ? 'bg-emerald-500' : 'bg-(--fg-subtle)'" />
|
||||
<span class="size-1.5 rounded-full" :class="enabled ? 'bg-emerald-500' : 'bg-fg-subtle'" />
|
||||
{{ enabled ? 'Mounted' : 'Off' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- A small focusable form to feel tab order with the guards active. -->
|
||||
<div class="rounded-xl border border-(--border) bg-(--bg-elevated) p-4 flex flex-col gap-3">
|
||||
<div class="demo-card p-4 flex flex-col gap-3">
|
||||
<div v-for="f in fields" :key="f.id" class="flex flex-col gap-1">
|
||||
<label class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)" :for="f.id">{{ f.label }}</label>
|
||||
<label class="demo-label" :for="f.id">{{ f.label }}</label>
|
||||
<input
|
||||
:id="f.id"
|
||||
:value="f.value"
|
||||
type="text"
|
||||
class="w-full rounded-lg border border-(--border) bg-(--bg) px-3 py-2 text-sm text-(--fg) placeholder:text-(--fg-subtle) transition focus:border-(--accent) focus:outline-none focus:ring-2 focus:ring-(--ring)"
|
||||
class="demo-input"
|
||||
@focus="focused = f.label"
|
||||
@blur="focused = null"
|
||||
>
|
||||
@@ -58,7 +58,7 @@ const fields = [
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center gap-1.5 rounded-lg border border-transparent bg-(--accent) px-3 py-1.5 text-sm font-medium text-(--accent-fg) transition hover:bg-(--accent-hover) active:scale-[0.98] cursor-pointer"
|
||||
class="demo-btn-primary"
|
||||
@focus="focused = 'Submit'"
|
||||
@blur="focused = null"
|
||||
>
|
||||
@@ -66,19 +66,19 @@ const fields = [
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 font-mono text-sm text-(--fg) tabular-nums">
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 font-mono text-sm text-fg tabular-nums">
|
||||
focused: {{ focused ?? 'nothing' }}
|
||||
</div>
|
||||
|
||||
<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:bg-(--bg-inset) hover:border-(--border-strong) active:scale-[0.98] cursor-pointer"
|
||||
class="demo-btn"
|
||||
@click="enabled = !enabled"
|
||||
>
|
||||
{{ enabled ? 'Remove focus guards' : 'Mount focus guards' }}
|
||||
</button>
|
||||
|
||||
<p class="text-xs text-(--fg-subtle)">
|
||||
<p class="text-xs text-fg-subtle">
|
||||
Guards are invisible <span class="font-mono">tabindex="0"</span> sentinels inserted at the page boundaries. Tab past the last field with them on to feel focus wrap around for overlays and modals.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -48,10 +48,10 @@ const activeId = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-md flex flex-col gap-4">
|
||||
<div class="demo-stack max-w-md">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">Scroll spy</span>
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border border-(--border) bg-(--bg-inset) px-2 py-0.5 text-xs font-medium text-(--fg-muted)">
|
||||
<span class="demo-label">Scroll spy</span>
|
||||
<span class="demo-badge">
|
||||
active: {{ activeId ?? '—' }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -64,8 +64,8 @@ const activeId = computed(() => {
|
||||
:key="s.id"
|
||||
class="flex items-center gap-2 rounded-lg border px-2.5 py-1.5 text-xs font-medium transition"
|
||||
:class="activeId === s.id
|
||||
? 'border-(--border-strong) bg-(--bg-inset) text-(--fg)'
|
||||
: 'border-transparent text-(--fg-muted)'"
|
||||
? 'border-border-strong bg-bg-inset text-fg'
|
||||
: 'border-transparent text-fg-muted'"
|
||||
>
|
||||
<span class="size-2 rounded-full transition" :class="[s.color, activeId === s.id ? 'opacity-100' : 'opacity-30']" />
|
||||
{{ s.label }}
|
||||
@@ -75,29 +75,29 @@ const activeId = computed(() => {
|
||||
<!-- Scrollable content; each section is an observed target. -->
|
||||
<div
|
||||
ref="root"
|
||||
class="h-56 flex-1 overflow-y-auto rounded-xl border border-(--border) bg-(--bg-inset) p-3 flex flex-col gap-3 scroll-smooth"
|
||||
class="h-56 flex-1 overflow-y-auto rounded-xl border border-border bg-bg-inset p-3 flex flex-col gap-3 scroll-smooth"
|
||||
>
|
||||
<section
|
||||
v-for="s in sections"
|
||||
:key="s.id"
|
||||
ref="itemEls"
|
||||
:data-id="s.id"
|
||||
class="rounded-lg border border-(--border) bg-(--bg-elevated) p-4 transition"
|
||||
:class="activeId === s.id ? 'ring-2 ring-(--ring)' : ''"
|
||||
class="rounded-lg border border-border bg-bg-elevated p-4 transition"
|
||||
:class="activeId === s.id ? 'ring-2 ring-ring' : ''"
|
||||
>
|
||||
<h3 class="text-sm font-semibold text-(--fg)">{{ s.label }}</h3>
|
||||
<p class="mt-1 text-xs text-(--fg-subtle)">
|
||||
<h3 class="text-sm font-semibold text-fg">{{ s.label }}</h3>
|
||||
<p class="mt-1 text-xs text-fg-subtle">
|
||||
Visibility: {{ Math.round((ratios[s.id] ?? 0) * 100) }}%
|
||||
</p>
|
||||
<div class="mt-2 h-16 rounded bg-(--bg-inset)" />
|
||||
<div class="mt-2 h-16 rounded bg-bg-inset" />
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<label class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)" for="io-threshold">Active threshold</label>
|
||||
<span class="font-mono text-xs tabular-nums text-(--fg-muted)">{{ Math.round(threshold * 100) }}%</span>
|
||||
<label class="demo-label" for="io-threshold">Active threshold</label>
|
||||
<span class="font-mono text-xs tabular-nums text-fg-muted">{{ Math.round(threshold * 100) }}%</span>
|
||||
</div>
|
||||
<input
|
||||
id="io-threshold"
|
||||
@@ -106,9 +106,9 @@ const activeId = computed(() => {
|
||||
min="0.1"
|
||||
max="0.9"
|
||||
step="0.1"
|
||||
class="w-full accent-(--accent) cursor-pointer"
|
||||
class="w-full accent-accent cursor-pointer"
|
||||
>
|
||||
<p class="text-xs text-(--fg-subtle)">A section becomes active once at least this much of it is visible inside the scroll root.</p>
|
||||
<p class="text-xs text-fg-subtle">A section becomes active once at least this much of it is visible inside the scroll root.</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -45,10 +45,10 @@ function removeLabel() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-sm flex flex-col gap-4">
|
||||
<div class="demo-stack max-w-sm">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">MutationObserver</span>
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border border-(--border) bg-(--bg-inset) px-2 py-0.5 text-xs font-medium text-(--fg-muted)">
|
||||
<span class="demo-label">MutationObserver</span>
|
||||
<span class="demo-badge">
|
||||
<span class="size-1.5 rounded-full transition" :class="isActive ? 'bg-emerald-500' : 'bg-amber-500'" />
|
||||
{{ isActive ? 'Observing' : 'Paused' }}
|
||||
</span>
|
||||
@@ -63,46 +63,46 @@ function removeLabel() {
|
||||
<div
|
||||
ref="target"
|
||||
class="rounded-xl border p-4 transition"
|
||||
:class="accent ? 'border-(--accent) bg-(--accent-subtle)' : 'border-(--border) bg-(--bg-elevated)'"
|
||||
:class="accent ? 'border-accent bg-accent-subtle' : 'border-border bg-bg-elevated'"
|
||||
:style="{ fontSize: `${fontSize}px` }"
|
||||
>
|
||||
<div class="mb-2 text-xs font-medium uppercase tracking-wide" :class="accent ? 'text-(--accent-text)' : 'text-(--fg-subtle)'">
|
||||
<div class="mb-2 text-xs font-medium uppercase tracking-wide" :class="accent ? 'text-accent-text' : 'text-fg-subtle'">
|
||||
Observed element
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<span
|
||||
v-for="(label, i) in labels"
|
||||
:key="`${label}-${i}`"
|
||||
class="inline-flex items-center rounded-md border border-(--border) bg-(--bg-inset) px-2 py-0.5 text-xs font-medium text-(--fg-muted)"
|
||||
class="inline-flex items-center rounded-md border border-border bg-bg-inset px-2 py-0.5 text-xs font-medium text-fg-muted"
|
||||
>
|
||||
{{ label }}
|
||||
</span>
|
||||
<span v-if="!labels.length" class="text-xs text-(--fg-subtle)">No labels</span>
|
||||
<span v-if="!labels.length" class="text-xs text-fg-subtle">No labels</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 text-center">
|
||||
<div class="font-mono text-3xl font-bold tabular-nums text-(--fg)">{{ mutationCount }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">mutations</div>
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 text-center">
|
||||
<div class="demo-stat text-3xl">{{ mutationCount }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">mutations</div>
|
||||
</div>
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 text-center">
|
||||
<div class="truncate font-mono text-sm font-bold text-(--fg)">{{ lastType }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">last record type</div>
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 text-center">
|
||||
<div class="truncate font-mono text-sm font-bold text-fg">{{ lastType }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">last record type</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<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:bg-(--bg-inset) hover:border-(--border-strong) active:scale-[0.98] cursor-pointer"
|
||||
class="demo-btn"
|
||||
@click="addLabel"
|
||||
>
|
||||
Add child
|
||||
</button>
|
||||
<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:bg-(--bg-inset) hover:border-(--border-strong) active:scale-[0.98] cursor-pointer disabled:cursor-not-allowed disabled:opacity-40 disabled:active:scale-100"
|
||||
class="demo-btn disabled:cursor-not-allowed disabled:opacity-40 disabled:active:scale-100"
|
||||
:disabled="!labels.length"
|
||||
@click="removeLabel"
|
||||
>
|
||||
@@ -110,31 +110,31 @@ function removeLabel() {
|
||||
</button>
|
||||
<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:bg-(--bg-inset) hover:border-(--border-strong) active:scale-[0.98] cursor-pointer"
|
||||
class="demo-btn"
|
||||
@click="accent = !accent"
|
||||
>
|
||||
Toggle class
|
||||
</button>
|
||||
<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:bg-(--bg-inset) hover:border-(--border-strong) active:scale-[0.98] cursor-pointer"
|
||||
class="demo-btn"
|
||||
@click="fontSize = fontSize === 14 ? 18 : 14"
|
||||
>
|
||||
Toggle style
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3">
|
||||
<div class="mb-1.5 text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">Recent records</div>
|
||||
<ul v-if="log.length" class="flex flex-col gap-1 font-mono text-xs text-(--fg)">
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3">
|
||||
<div class="demo-label mb-1.5">Recent records</div>
|
||||
<ul v-if="log.length" class="flex flex-col gap-1 font-mono text-xs text-fg">
|
||||
<li v-for="(entry, i) in log" :key="i" class="truncate">{{ entry }}</li>
|
||||
</ul>
|
||||
<p v-else class="font-mono text-xs text-(--fg-subtle)">Mutate the element above to record changes.</p>
|
||||
<p v-else class="font-mono text-xs text-fg-subtle">Mutate the element above to record changes.</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center gap-1.5 rounded-lg border border-transparent bg-(--accent) px-3 py-1.5 text-sm font-medium text-(--accent-fg) transition hover:bg-(--accent-hover) active:scale-[0.98] cursor-pointer"
|
||||
class="demo-btn-primary"
|
||||
@click="isActive ? pause() : resume()"
|
||||
>
|
||||
{{ isActive ? 'Pause observer' : 'Resume observer' }}
|
||||
|
||||
@@ -26,73 +26,73 @@ const parentInfo = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-sm flex flex-col gap-4">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">parentElement</span>
|
||||
<div class="demo-stack max-w-sm">
|
||||
<span class="demo-label">parentElement</span>
|
||||
|
||||
<!-- Two candidate parents; the child is teleported between them via v-if -->
|
||||
<div class="flex flex-col gap-3">
|
||||
<div
|
||||
id="wrapper-a"
|
||||
class="rounded-xl border border-dashed p-3 transition"
|
||||
:class="!inSecond ? 'border-(--accent) bg-(--accent-subtle)' : 'border-(--border) bg-(--bg-elevated)'"
|
||||
:class="!inSecond ? 'border-accent bg-accent-subtle' : 'border-border bg-bg-elevated'"
|
||||
>
|
||||
<div class="mb-2 text-xs font-medium uppercase tracking-wide" :class="!inSecond ? 'text-(--accent-text)' : 'text-(--fg-subtle)'">
|
||||
<div class="mb-2 text-xs font-medium uppercase tracking-wide" :class="!inSecond ? 'text-accent-text' : 'text-fg-subtle'">
|
||||
#wrapper-a
|
||||
</div>
|
||||
<div
|
||||
v-if="!inSecond"
|
||||
ref="child"
|
||||
class="rounded-lg border border-(--border) bg-(--bg-inset) px-3 py-2 text-sm font-medium text-(--fg)"
|
||||
class="rounded-lg border border-border bg-bg-inset px-3 py-2 text-sm font-medium text-fg"
|
||||
>
|
||||
Tracked child
|
||||
</div>
|
||||
<div v-else class="text-xs text-(--fg-subtle)">empty</div>
|
||||
<div v-else class="text-xs text-fg-subtle">empty</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="wrapper-b"
|
||||
class="rounded-xl border border-dashed p-3 transition"
|
||||
:class="inSecond ? 'border-(--accent) bg-(--accent-subtle)' : 'border-(--border) bg-(--bg-elevated)'"
|
||||
:class="inSecond ? 'border-accent bg-accent-subtle' : 'border-border bg-bg-elevated'"
|
||||
>
|
||||
<div class="mb-2 text-xs font-medium uppercase tracking-wide" :class="inSecond ? 'text-(--accent-text)' : 'text-(--fg-subtle)'">
|
||||
<div class="mb-2 text-xs font-medium uppercase tracking-wide" :class="inSecond ? 'text-accent-text' : 'text-fg-subtle'">
|
||||
#wrapper-b
|
||||
</div>
|
||||
<div
|
||||
v-if="inSecond"
|
||||
ref="child"
|
||||
class="rounded-lg border border-(--border) bg-(--bg-inset) px-3 py-2 text-sm font-medium text-(--fg)"
|
||||
class="rounded-lg border border-border bg-bg-inset px-3 py-2 text-sm font-medium text-fg"
|
||||
>
|
||||
Tracked child
|
||||
</div>
|
||||
<div v-else class="text-xs text-(--fg-subtle)">empty</div>
|
||||
<div v-else class="text-xs text-fg-subtle">empty</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center gap-1.5 rounded-lg border border-transparent bg-(--accent) px-3 py-1.5 text-sm font-medium text-(--accent-fg) transition hover:bg-(--accent-hover) active:scale-[0.98] cursor-pointer"
|
||||
class="demo-btn-primary"
|
||||
@click="inSecond = !inSecond"
|
||||
>
|
||||
Move child to #wrapper-{{ inSecond ? 'a' : 'b' }}
|
||||
</button>
|
||||
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 font-mono text-sm text-(--fg) tabular-nums">
|
||||
<div class="mb-2 font-sans text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">Resolved parent</div>
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 font-mono text-sm text-fg tabular-nums">
|
||||
<div class="demo-label mb-2 font-sans">Resolved parent</div>
|
||||
<template v-if="parentInfo">
|
||||
<div class="flex justify-between gap-2">
|
||||
<span class="text-(--fg-subtle)">tag</span>
|
||||
<span class="text-(--accent-text)"><{{ parentInfo.tag }}></span>
|
||||
<span class="text-fg-subtle">tag</span>
|
||||
<span class="text-accent-text"><{{ parentInfo.tag }}></span>
|
||||
</div>
|
||||
<div class="flex justify-between gap-2">
|
||||
<span class="text-(--fg-subtle)">id</span>
|
||||
<span class="text-fg-subtle">id</span>
|
||||
<span class="truncate">{{ parentInfo.id }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between gap-2">
|
||||
<span class="text-(--fg-subtle)">size</span>
|
||||
<span class="text-fg-subtle">size</span>
|
||||
<span>{{ parentInfo.width }} × {{ parentInfo.height }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="font-sans text-xs text-(--fg-subtle)">Resolving parent on the client…</div>
|
||||
<div v-else class="font-sans text-xs text-fg-subtle">Resolving parent on the client…</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -22,10 +22,10 @@ const { isSupported, isActive, pause, resume } = useResizeObserver(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-sm flex flex-col gap-4">
|
||||
<div class="demo-stack max-w-sm">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">ResizeObserver</span>
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border border-(--border) bg-(--bg-inset) px-2 py-0.5 text-xs font-medium text-(--fg-muted)">
|
||||
<span class="demo-label">ResizeObserver</span>
|
||||
<span class="demo-badge">
|
||||
<span class="size-1.5 rounded-full transition" :class="isActive ? 'bg-emerald-500' : 'bg-amber-500'" />
|
||||
{{ isActive ? 'Observing' : 'Paused' }}
|
||||
</span>
|
||||
@@ -39,40 +39,40 @@ const { isSupported, isActive, pause, resume } = useResizeObserver(
|
||||
<!-- Drag the bottom-right handle to resize; the observer reports new dimensions -->
|
||||
<div
|
||||
ref="target"
|
||||
class="relative grid min-h-32 min-w-40 max-w-full resize overflow-auto rounded-xl border border-(--border) bg-(--bg-elevated) p-4 place-items-center"
|
||||
class="demo-card relative grid min-h-32 min-w-40 max-w-full resize overflow-auto p-4 place-items-center"
|
||||
style="width: 16rem; height: 8rem;"
|
||||
>
|
||||
<div class="pointer-events-none select-none text-center">
|
||||
<div class="font-mono text-3xl font-bold tabular-nums text-(--fg)">
|
||||
{{ size.width }}<span class="text-(--fg-subtle)"> × </span>{{ size.height }}
|
||||
<div class="demo-stat text-3xl">
|
||||
{{ size.width }}<span class="text-fg-subtle"> × </span>{{ size.height }}
|
||||
</div>
|
||||
<div class="mt-1 text-xs text-(--fg-subtle)">drag the bottom-right corner</div>
|
||||
<div class="mt-1 text-xs text-fg-subtle">drag the bottom-right corner</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-2 text-center">
|
||||
<div class="font-mono text-lg font-bold tabular-nums text-(--fg)">{{ size.width }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">width px</div>
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-2 text-center">
|
||||
<div class="demo-stat text-lg">{{ size.width }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">width px</div>
|
||||
</div>
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-2 text-center">
|
||||
<div class="font-mono text-lg font-bold tabular-nums text-(--fg)">{{ size.height }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">height px</div>
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-2 text-center">
|
||||
<div class="demo-stat text-lg">{{ size.height }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">height px</div>
|
||||
</div>
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-2 text-center">
|
||||
<div class="font-mono text-lg font-bold tabular-nums text-(--fg)">{{ callbacks }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">callbacks</div>
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-2 text-center">
|
||||
<div class="demo-stat text-lg">{{ callbacks }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">callbacks</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center gap-1.5 rounded-lg border border-transparent bg-(--accent) px-3 py-1.5 text-sm font-medium text-(--accent-fg) transition hover:bg-(--accent-hover) active:scale-[0.98] cursor-pointer"
|
||||
class="demo-btn-primary"
|
||||
@click="isActive ? pause() : resume()"
|
||||
>
|
||||
{{ isActive ? 'Pause observer' : 'Resume observer' }}
|
||||
</button>
|
||||
<p class="text-xs text-(--fg-subtle)">
|
||||
<p class="text-xs text-fg-subtle">
|
||||
While paused, resizing won't update the readout until you resume.
|
||||
</p>
|
||||
</template>
|
||||
|
||||
@@ -14,39 +14,39 @@ watch(focused, (isFocused) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-sm flex flex-col gap-4">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">Window focus</span>
|
||||
<div class="demo-stack max-w-sm">
|
||||
<span class="demo-label">Window focus</span>
|
||||
|
||||
<div
|
||||
class="rounded-xl border p-6 text-center transition"
|
||||
:class="focused
|
||||
? 'border-emerald-500/30 bg-emerald-500/10'
|
||||
: 'border-(--border) bg-(--bg-inset)'"
|
||||
: 'border-border bg-bg-inset'"
|
||||
>
|
||||
<div
|
||||
class="mx-auto flex size-14 items-center justify-center rounded-full transition"
|
||||
:class="focused
|
||||
? 'bg-emerald-500/15 text-emerald-600 dark:text-emerald-400'
|
||||
: 'bg-(--bg-elevated) text-(--fg-subtle)'"
|
||||
: 'bg-bg-elevated text-fg-subtle'"
|
||||
>
|
||||
<span class="size-3 rounded-full transition" :class="focused ? 'bg-emerald-500 animate-pulse' : 'bg-(--fg-subtle)'" />
|
||||
<span class="size-3 rounded-full transition" :class="focused ? 'bg-emerald-500 animate-pulse' : 'bg-fg-subtle'" />
|
||||
</div>
|
||||
<div class="mt-3 text-lg font-semibold text-(--fg)">
|
||||
<div class="mt-3 text-lg font-semibold text-fg">
|
||||
{{ focused ? 'Window focused' : 'Window blurred' }}
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-(--fg-muted)">
|
||||
<p class="mt-1 text-sm text-fg-muted">
|
||||
{{ focused ? 'Click outside or switch apps to blur.' : 'Click back into this window to refocus.' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 text-center">
|
||||
<div class="font-mono text-3xl font-bold tabular-nums text-(--fg)">{{ focused ? 'on' : 'off' }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">focused</div>
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 text-center">
|
||||
<div class="demo-stat text-3xl">{{ focused ? 'on' : 'off' }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">focused</div>
|
||||
</div>
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 text-center">
|
||||
<div class="font-mono text-3xl font-bold tabular-nums text-(--fg)">{{ blurCount }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">times blurred</div>
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 text-center">
|
||||
<div class="demo-stat text-3xl">{{ blurCount }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">times blurred</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -25,53 +25,53 @@ function scrollToBottom() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full max-w-sm flex flex-col gap-4">
|
||||
<div class="demo-stack max-w-sm">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">Window scroll</span>
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border border-(--border) bg-(--bg-inset) px-2 py-0.5 text-xs font-medium text-(--fg-muted)">
|
||||
<span class="size-1.5 rounded-full transition" :class="isScrolling ? 'bg-emerald-500 animate-pulse' : 'bg-(--fg-subtle)'" />
|
||||
<span class="demo-label">Window scroll</span>
|
||||
<span class="demo-badge">
|
||||
<span class="size-1.5 rounded-full transition" :class="isScrolling ? 'bg-emerald-500 animate-pulse' : 'bg-fg-subtle'" />
|
||||
{{ isScrolling ? 'Scrolling' : 'Idle' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 text-center">
|
||||
<div class="font-mono text-3xl font-bold tabular-nums text-(--fg)">{{ Math.round(x) }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">scroll x</div>
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 text-center">
|
||||
<div class="demo-stat text-3xl">{{ Math.round(x) }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">scroll x</div>
|
||||
</div>
|
||||
<div class="rounded-lg border border-(--border) bg-(--bg-inset) p-3 text-center">
|
||||
<div class="font-mono text-3xl font-bold tabular-nums text-(--fg)">{{ Math.round(y) }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-(--fg-subtle)">scroll y</div>
|
||||
<div class="rounded-lg border border-border bg-bg-inset p-3 text-center">
|
||||
<div class="demo-stat text-3xl">{{ Math.round(y) }}</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-fg-subtle">scroll y</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-(--border) bg-(--bg-elevated) p-4 flex flex-col gap-3">
|
||||
<div class="demo-card p-4 flex flex-col gap-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">Direction</span>
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border border-(--border) bg-(--bg-inset) px-2 py-0.5 text-xs font-medium text-(--fg-muted)">
|
||||
<span class="demo-label">Direction</span>
|
||||
<span class="demo-badge">
|
||||
{{ verticalDirection }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex items-center justify-between text-sm">
|
||||
<span class="text-(--fg-muted)">Arrived at top</span>
|
||||
<span class="text-fg-muted">Arrived at top</span>
|
||||
<span
|
||||
class="inline-flex items-center gap-1.5 rounded-md px-2 py-0.5 text-xs font-medium"
|
||||
:class="arrivedState.top
|
||||
? 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400'
|
||||
: 'bg-(--bg-inset) text-(--fg-subtle)'"
|
||||
: 'bg-bg-inset text-fg-subtle'"
|
||||
>
|
||||
{{ arrivedState.top ? 'yes' : 'no' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between text-sm">
|
||||
<span class="text-(--fg-muted)">Arrived at bottom</span>
|
||||
<span class="text-fg-muted">Arrived at bottom</span>
|
||||
<span
|
||||
class="inline-flex items-center gap-1.5 rounded-md px-2 py-0.5 text-xs font-medium"
|
||||
:class="arrivedState.bottom
|
||||
? 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400'
|
||||
: 'bg-(--bg-inset) text-(--fg-subtle)'"
|
||||
: 'bg-bg-inset text-fg-subtle'"
|
||||
>
|
||||
{{ arrivedState.bottom ? 'yes' : 'no' }}
|
||||
</span>
|
||||
@@ -82,7 +82,7 @@ function scrollToBottom() {
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center gap-1.5 rounded-lg border border-transparent bg-(--accent) px-3 py-1.5 text-sm font-medium text-(--accent-fg) transition hover:bg-(--accent-hover) active:scale-[0.98] cursor-pointer disabled:cursor-not-allowed disabled:opacity-40 disabled:active:scale-100"
|
||||
class="demo-btn-primary disabled:cursor-not-allowed disabled:opacity-40 disabled:active:scale-100"
|
||||
:disabled="arrivedState.top"
|
||||
@click="scrollToTop"
|
||||
>
|
||||
@@ -90,7 +90,7 @@ function scrollToBottom() {
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center gap-1.5 rounded-lg border border-transparent bg-(--accent) px-3 py-1.5 text-sm font-medium text-(--accent-fg) transition hover:bg-(--accent-hover) active:scale-[0.98] cursor-pointer disabled:cursor-not-allowed disabled:opacity-40 disabled:active:scale-100"
|
||||
class="demo-btn-primary disabled:cursor-not-allowed disabled:opacity-40 disabled:active:scale-100"
|
||||
:disabled="arrivedState.bottom"
|
||||
@click="scrollToBottom"
|
||||
>
|
||||
@@ -100,12 +100,12 @@ function scrollToBottom() {
|
||||
|
||||
<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:bg-(--bg-inset) hover:border-(--border-strong) active:scale-[0.98] cursor-pointer"
|
||||
class="demo-btn"
|
||||
@click="measure"
|
||||
>
|
||||
Re-measure
|
||||
</button>
|
||||
<p class="text-xs text-(--fg-subtle)">
|
||||
<p class="text-xs text-fg-subtle">
|
||||
Scroll the documentation page to watch the position and arrived edges update live.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -13,57 +13,57 @@ const ratioPercent = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex w-full max-w-sm flex-col gap-4">
|
||||
<div class="demo-stack max-w-sm">
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div class="rounded-xl border border-(--border) bg-(--bg-elevated) p-4 text-center">
|
||||
<p class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">
|
||||
<div class="demo-card p-4 text-center">
|
||||
<p class="demo-label">
|
||||
Width
|
||||
</p>
|
||||
<p class="font-mono text-3xl font-bold tabular-nums text-(--fg)">
|
||||
<p class="demo-stat text-3xl">
|
||||
{{ width }}
|
||||
</p>
|
||||
<p class="text-xs text-(--fg-subtle)">
|
||||
<p class="text-xs text-fg-subtle">
|
||||
px
|
||||
</p>
|
||||
</div>
|
||||
<div class="rounded-xl border border-(--border) bg-(--bg-elevated) p-4 text-center">
|
||||
<p class="text-xs font-medium uppercase tracking-wide text-(--fg-subtle)">
|
||||
<div class="demo-card p-4 text-center">
|
||||
<p class="demo-label">
|
||||
Height
|
||||
</p>
|
||||
<p class="font-mono text-3xl font-bold tabular-nums text-(--fg)">
|
||||
<p class="demo-stat text-3xl">
|
||||
{{ height }}
|
||||
</p>
|
||||
<p class="text-xs text-(--fg-subtle)">
|
||||
<p class="text-xs text-fg-subtle">
|
||||
px
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-(--border) bg-(--bg-elevated) p-4">
|
||||
<div class="demo-card p-4">
|
||||
<div
|
||||
class="relative flex items-center justify-center overflow-hidden rounded-lg border border-(--border-strong) bg-(--bg-inset) transition-all"
|
||||
class="relative flex items-center justify-center overflow-hidden rounded-lg border border-border-strong bg-bg-inset transition-all"
|
||||
:style="{ aspectRatio: `${Math.max(width, 1)} / ${Math.max(height, 1)}` }"
|
||||
>
|
||||
<div
|
||||
class="absolute inset-y-0 left-0 bg-(--accent-subtle) transition-all"
|
||||
class="absolute inset-y-0 left-0 bg-accent-subtle transition-all"
|
||||
:style="{ width: `${ratioPercent}%` }"
|
||||
/>
|
||||
<span class="relative font-mono text-xs text-(--fg-muted) tabular-nums">
|
||||
<span class="relative font-mono text-xs text-fg-muted tabular-nums">
|
||||
{{ width }} × {{ height }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center justify-center gap-2">
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border border-(--border) bg-(--bg-inset) px-2 py-0.5 text-xs font-medium text-(--fg-muted)">
|
||||
<span class="demo-badge">
|
||||
{{ orientation }}
|
||||
</span>
|
||||
<span class="inline-flex items-center gap-1.5 rounded-md border border-(--border) bg-(--bg-inset) px-2 py-0.5 text-xs font-medium text-(--fg-muted)">
|
||||
<span class="demo-badge">
|
||||
ratio {{ aspect }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="text-center text-xs text-(--fg-subtle)">
|
||||
<p class="text-center text-xs text-fg-subtle">
|
||||
Resize your browser window to watch the values update live.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user