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:
@@ -0,0 +1,90 @@
|
||||
<script lang="ts">
|
||||
export type FlowBackgroundVariant = 'dots' | 'lines' | 'cross';
|
||||
|
||||
/**
|
||||
* A grid background drawn as an SVG `<pattern>` that pans and zooms with the
|
||||
* viewport (the pattern origin shifts by `viewport % (gap·zoom)` and its cell
|
||||
* scales by `zoom`). Sits behind the viewport layer, ignores pointer events, and
|
||||
* is fully styleable via `[data-flow-background]` / `currentColor`.
|
||||
*/
|
||||
export interface FlowBackgroundProps {
|
||||
/** Pattern style. @default 'dots' */
|
||||
variant?: FlowBackgroundVariant;
|
||||
/** Grid spacing in flow units (single value or `[x, y]`). @default 20 */
|
||||
gap?: number | [number, number];
|
||||
/** Dot radius / line thickness in px. @default 1 */
|
||||
size?: number;
|
||||
/** Pattern colour. @default 'currentColor' */
|
||||
color?: string;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useFlowContext } from './context';
|
||||
|
||||
const { variant = 'dots', gap = 20, size = 1, color = 'currentColor' } = defineProps<FlowBackgroundProps>();
|
||||
|
||||
const ctx = useFlowContext();
|
||||
const patternId = computed(() => `${ctx.flowId}__bg`);
|
||||
|
||||
const gapXY = computed<[number, number]>(() => (Array.isArray(gap) ? gap : [gap, gap]));
|
||||
|
||||
const scaled = computed(() => {
|
||||
const vp = ctx.viewport.value;
|
||||
return {
|
||||
w: gapXY.value[0] * vp.zoom,
|
||||
h: gapXY.value[1] * vp.zoom,
|
||||
x: vp.x % (gapXY.value[0] * vp.zoom),
|
||||
y: vp.y % (gapXY.value[1] * vp.zoom),
|
||||
s: size * vp.zoom,
|
||||
};
|
||||
});
|
||||
|
||||
// Path for line / cross variants, drawn at the cell origin.
|
||||
const linePath = computed(() => {
|
||||
const { w, h } = scaled.value;
|
||||
return variant === 'cross'
|
||||
? `M ${w / 2} ${h / 2 - 3} V ${h / 2 + 3} M ${w / 2 - 3} ${h / 2} H ${w / 2 + 3}`
|
||||
: `M ${w} 0 H 0 V ${h}`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
data-flow-background=""
|
||||
:data-variant="variant"
|
||||
:style="{ position: 'absolute', inset: '0', width: '100%', height: '100%', pointerEvents: 'none', color }"
|
||||
>
|
||||
<pattern
|
||||
:id="patternId"
|
||||
:x="scaled.x"
|
||||
:y="scaled.y"
|
||||
:width="scaled.w"
|
||||
:height="scaled.h"
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<circle
|
||||
v-if="variant === 'dots'"
|
||||
:cx="scaled.w / 2"
|
||||
:cy="scaled.h / 2"
|
||||
:r="scaled.s"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
v-else
|
||||
:d="linePath"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
:stroke-width="scaled.s"
|
||||
/>
|
||||
</pattern>
|
||||
<rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="100%"
|
||||
height="100%"
|
||||
:fill="`url(#${patternId})`"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
Reference in New Issue
Block a user