项目文件夹

文件
wehub-resource-sync c48612c494
CI / E2E Tests (push) Has been cancelled
CI / Lint, Typecheck & Unit Tests (push) Has been cancelled
Docs Build / Build docs site (push) Has been cancelled
Publish @openmaic packages / Build, validate & publish (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:03:23 +08:00

65 行
2.7 KiB
TypeScript

'use client';
import { Plus } from 'lucide-react';
import { cn } from '@/lib/utils';
interface InsertionZoneProps {
readonly label: string;
readonly onInsert: () => void;
}
/**
* Hover-revealed insertion affordance between two thumbs.
*
* The gap is a slim 8px hit zone that matches playback `SceneSidebar`'s
* `space-y-2` density (no layout shift, ever). On hover the `+` badge
* pops out to the right side of the gap with a small overshoot, sitting
* on its own z-layer with a solid background + soft drop shadow so it
* clearly floats above any adjacent violet ring.
*/
export function InsertionZone({ label, onInsert }: InsertionZoneProps) {
// `z-20` lifts the whole zone above adjacent `Reorder.Item` siblings.
// Without this, the next-in-DOM-order ThumbItem (which has a `transform`
// via motion's Reorder, creating its own stacking context) paints on
// top, and its violet ring clips through the `+` badge regardless of
// any z-index applied inside the InsertionZone itself.
return (
<div className="group/insert relative isolate z-20 h-2 cursor-pointer overflow-visible">
<button
type="button"
onClick={onInsert}
aria-label={label}
title={label}
data-testid="slide-nav-insert"
className="absolute inset-0 z-10 outline-none focus-visible:opacity-100"
>
<span className="sr-only">{label}</span>
</button>
<span
aria-hidden
className={cn(
// Anchored at the right edge of the gap, vertically centered.
// z-30 lifts it above the adjacent active tile's ring (z-default).
'pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 z-30',
'inline-flex h-5 w-5 items-center justify-center rounded-full',
// Solid background + ring + shadow gives it real visual
// elevation against the neighbouring violet ring zones.
'bg-white text-violet-600 ring-1 ring-violet-200',
'dark:bg-zinc-900 dark:text-violet-300 dark:ring-violet-400/40',
'shadow-md shadow-violet-500/15 dark:shadow-violet-500/20',
// Popup motion: start tiny + transparent, end full size with a
// small overshoot. The custom cubic-bezier is a classic
// "back-ease-out" giving it a quick, springy reveal.
'opacity-0 scale-50',
'group-hover/insert:opacity-100 group-hover/insert:scale-100',
'group-focus-within/insert:opacity-100 group-focus-within/insert:scale-100',
'transition-[opacity,transform] duration-200',
'[transition-timing-function:cubic-bezier(0.34,1.56,0.64,1)]',
)}
>
<Plus className="h-3 w-3" strokeWidth={2.5} />
</span>
</div>
);
}