feat(primitives): media-editor components, category reorg, perf + type cleanup

Reorganize components into category folders (forms/canvas/overlays/etc.); add the
media-editor headless family (timeline, curve-editor, waveform, crop, color
picker, etc.); apply perf fixes (O(1) collection lookups, plain-object drag
state, gesture-leak teardown, shallowRef color state, rect caching) and replace
source `any` with proper types.
This commit is contained in:
2026-06-15 16:54:29 +07:00
parent 661a55719e
commit eefd7abf83
1029 changed files with 65815 additions and 9449 deletions
+103
View File
@@ -0,0 +1,103 @@
<script setup lang="ts">
import { ref } from 'vue';
import {
QrCodeBackground,
QrCodeCells,
QrCodeLogo,
QrCodeMarker,
QrCodeMarkers,
QrCodeRoot,
} from '@robonen/primitives';
import type { QrCellPattern, QrMarkerBall, QrMarkerFrame } from '@robonen/primitives';
const value = ref('https://github.com/robonen/tools');
const pattern = ref<QrCellPattern>('fluid');
const frame = ref<QrMarkerFrame>('rounded');
const ball = ref<QrMarkerBall>('circle');
const patterns: QrCellPattern[] = ['square', 'dot', 'rounded', 'fluid'];
const frames: QrMarkerFrame[] = ['square', 'rounded', 'circle'];
const balls: QrMarkerBall[] = ['square', 'rounded', 'circle', 'diamond'];
</script>
<template>
<div class="flex flex-col gap-6 text-fg">
<!-- Interactive playground -->
<div class="demo-card flex flex-col items-center gap-5 p-6 sm:flex-row sm:items-start">
<QrCodeRoot
:value="value"
error-correction="H"
class="size-48 shrink-0 text-fg"
>
<QrCodeBackground fill="transparent" :radius="2" />
<QrCodeCells :pattern="pattern" :radius="0.6" class="fill-current" />
<QrCodeMarkers :frame="frame" :ball="ball" :radius="0.5" class="fill-accent" />
<QrCodeLogo v-slot="{ cx, cy, width }" :size="0.22" :padding="1.5">
<circle :cx="cx" :cy="cy" :r="width / 2" class="fill-bg-elevated" />
<text
:x="cx"
:y="cy"
text-anchor="middle"
dominant-baseline="central"
:font-size="width * 0.7"
class="fill-accent"
style="font-family: ui-sans-serif, system-ui; font-weight: 700"
>
R
</text>
</QrCodeLogo>
</QrCodeRoot>
<div class="flex w-full flex-col gap-3 text-sm">
<label class="flex flex-col gap-1">
<span class="text-fg-muted">Value</span>
<input
v-model="value"
class="rounded-md border border-border bg-bg-inset px-2 py-1"
>
</label>
<label class="flex flex-col gap-1">
<span class="text-fg-muted">Pattern</span>
<select v-model="pattern" class="rounded-md border border-border bg-bg-inset px-2 py-1">
<option v-for="p in patterns" :key="p" :value="p">{{ p }}</option>
</select>
</label>
<div class="flex gap-3">
<label class="flex flex-1 flex-col gap-1">
<span class="text-fg-muted">Frame</span>
<select v-model="frame" class="rounded-md border border-border bg-bg-inset px-2 py-1">
<option v-for="f in frames" :key="f" :value="f">{{ f }}</option>
</select>
</label>
<label class="flex flex-1 flex-col gap-1">
<span class="text-fg-muted">Ball</span>
<select v-model="ball" class="rounded-md border border-border bg-bg-inset px-2 py-1">
<option v-for="b in balls" :key="b" :value="b">{{ b }}</option>
</select>
</label>
</div>
</div>
</div>
<!-- A few canned styles showing per-corner control and gradients -->
<div class="grid grid-cols-2 gap-4 sm:grid-cols-3">
<QrCodeRoot value="dots" class="size-32 text-emerald-500">
<QrCodeCells pattern="dot" :gap="0.2" class="fill-current" />
<QrCodeMarkers frame="circle" ball="circle" class="fill-current" />
</QrCodeRoot>
<QrCodeRoot value="fluid" class="size-32 text-indigo-500">
<QrCodeCells pattern="fluid" :radius="1" class="fill-current" />
<QrCodeMarkers frame="rounded" ball="rounded" :radius="0.6" class="fill-current" />
</QrCodeRoot>
<QrCodeRoot value="per-corner" class="size-32">
<QrCodeCells pattern="rounded" :radius="0.4" class="fill-neutral-800 dark:fill-neutral-100" />
<QrCodeMarker corner="top-left" frame="circle" ball="circle" class="fill-rose-500" />
<QrCodeMarker corner="top-right" frame="rounded" ball="diamond" :radius="0.5" class="fill-amber-500" />
<QrCodeMarker corner="bottom-left" frame="square" ball="square" class="fill-sky-500" />
</QrCodeRoot>
</div>
</div>
</template>