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
@@ -0,0 +1,97 @@
<script lang="ts">
import type { PrimitiveProps } from '../../internal/primitive';
/**
* The scrollable region that clips and natively scrolls its content while the OS scrollbars
* are visually hidden. Place all scrollable content inside it; it must be a child of
* `ScrollAreaRoot`.
*/
export interface ScrollAreaViewportProps extends PrimitiveProps {
/**
* Inline `nonce` attribute applied to the injected style tag (CSP support).
* Falls back to the `ConfigProvider` `nonce` when omitted.
*/
nonce?: string;
/**
* `tabindex` applied to the scrollable region so keyboard users can focus the
* panel and arrow-scroll natively even when no scrollbar is interactive.
* Pass `-1`/`undefined` to opt out.
* @default 0
*/
tabindex?: number;
}
</script>
<script setup lang="ts">
import { computed, onMounted, shallowRef, toRef, watch } from 'vue';
import { Primitive } from '../../internal/primitive';
import { useForwardExpose } from '@robonen/vue';
import { useNonce } from '../../utilities/config-provider';
import { useScrollAreaRootContext } from './context';
defineOptions({ inheritAttrs: false });
const props = withDefaults(defineProps<ScrollAreaViewportProps>(), {
tabindex: 0,
});
const ctx = useScrollAreaRootContext();
const { forwardRef, currentElement } = useForwardExpose();
const nonce = useNonce(toRef(() => props.nonce));
/**
* Only force `fit-content` when horizontal scrolling is enabled. In the common
* vertical-only case leaving `minWidth` unset lets the parent constrain width
* so `text-overflow: ellipsis` keeps working inside the content wrapper.
*/
const contentMinWidth = computed(() =>
ctx.scrollbarXEnabled.value ? 'fit-content' : '100%',
);
const contentRef = shallowRef<HTMLElement | null>(null);
watch(currentElement, (el) => {
ctx.viewport.value = el ?? null;
}, { immediate: true });
watch(contentRef, (el) => {
ctx.content.value = el ?? null;
}, { immediate: true });
onMounted(() => {
ctx.viewport.value = currentElement.value ?? null;
ctx.content.value = contentRef.value ?? null;
});
</script>
<template>
<!-- Hide native scrollbars while preserving native scrolling behaviour. -->
<component
:is="'style'"
:nonce="nonce"
>
[data-scroll-area-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-scroll-area-viewport]::-webkit-scrollbar{display:none;}
</component>
<Primitive
:ref="forwardRef"
:as="as"
:id="($attrs.id as string | undefined) ?? ctx.viewportId.value"
data-scroll-area-viewport=""
:tabindex="tabindex"
:style="{
overflowX: ctx.scrollbarXEnabled.value ? 'scroll' : 'hidden',
overflowY: ctx.scrollbarYEnabled.value ? 'scroll' : 'hidden',
}"
v-bind="$attrs"
>
<!-- A `min-width: fit-content` inner ensures horizontal content is measurable. -->
<div
:ref="(el: any) => { contentRef = el; }"
:style="{ minWidth: contentMinWidth, display: 'table' }"
>
<slot />
</div>
</Primitive>
</template>