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,74 @@
<script lang="ts">
import type { SelectContentImplEmits, SelectContentImplProps } from './SelectContentImpl.vue';
/**
* The floating panel that holds the options. While open it mounts
* `SelectContentImpl` behind `Presence` (so it can animate in and out); while
* closed it still renders the options into a detached `DocumentFragment` so each
* `SelectItem` registers its value/label and `SelectValue` shows the
* initially-selected label before the dropdown is ever opened. Usually placed
* inside a `SelectPortal` and contains a `SelectViewport` of `SelectItem`s.
*/
export interface SelectContentProps extends SelectContentImplProps {
/**
* Force mounting (keeps the panel in the DOM) for externally-controlled
* animation libraries.
*/
forceMount?: boolean;
}
export type SelectContentEmits = SelectContentImplEmits;
</script>
<script setup lang="ts">
import { computed, onMounted, ref, shallowRef, watch } from 'vue';
import { Presence } from '../../utilities/presence';
import { useSelectRootContext } from './context';
import SelectContentImpl from './SelectContentImpl.vue';
import SelectProvider from './SelectProvider.vue';
const props = defineProps<SelectContentProps>();
const emit = defineEmits<SelectContentEmits>();
const rootCtx = useSelectRootContext();
const present = computed(() => props.forceMount || rootCtx.open.value);
// Delay toggling render-presence so children re-render with the latest state
// before the exit transition runs (state-based `data-state=closed` animations).
const renderPresence = ref(present.value);
function syncRenderPresence() {
renderPresence.value = present.value;
}
watch(present, () => {
setTimeout(syncRenderPresence);
});
// Detached fragment that keeps options mounted (and registered) while closed.
const fragment = shallowRef<DocumentFragment | undefined>(undefined);
onMounted(() => {
fragment.value = typeof DocumentFragment !== 'undefined' ? new DocumentFragment() : undefined;
});
</script>
<template>
<Presence
v-if="present || renderPresence"
:present="present"
>
<SelectContentImpl
v-bind="props"
@close-auto-focus="emit('closeAutoFocus', $event)"
@escape-key-down="emit('escapeKeyDown', $event)"
@pointer-down-outside="emit('pointerDownOutside', $event)"
>
<slot />
</SelectContentImpl>
</Presence>
<Teleport v-else-if="fragment" :to="fragment">
<SelectProvider :context="rootCtx">
<slot />
</SelectProvider>
</Teleport>
</template>