Components/FlipOverlay

FlipOverlay

3D flip overlay that expands from a trigger

VerifiedSince 0.3.4

Usage

Loading demo...

Best Practices

  • Pass the real trigger element or its DOMRect as source; null falls back to the centered card without origin continuity.
  • Keep duration near the default for stacked overlays so shared mask and card motion stay synchronized.
  • Use cardStyle for size constraints such as width and maxHeight; use cardClass for reusable visual variants.
  • Prefer surface="mask" for normal cards, glass / refraction only when the backdrop remains readable, and pure for fully custom card styling.
  • Use #header-display, #header-actions, or #header-close before replacing the whole header; full #header opts out of built-in close layout.

API Reference

Props

NameTypeDefaultDescription
modelValuebooleanfalseVisible state (v-model)
sourceHTMLElement | DOMRect | nullnullAnimation origin
sourceRadiusstring | nullnullOrigin border radius
durationnumber480Animation duration (ms)
perspectivenumber12003D perspective
rotateXnumber6X-axis rotation
rotateYnumber8Y-axis rotation
randomTiltbooleantrueRandom tilt per open
tiltRangenumber2Random tilt range
easeOutstring'back.out(1.25)'Open easing
easeInstring'back.in(1)'Close easing
maskClosablebooleantrueClick mask to close
preventAccidentalClosebooleanfalseAccidental-close protection (block mask close + intercept page exit + red warning glow)
globalMaskbooleantrueWhether to render the global visual mask layer
surface'pure' | 'mask' | 'blur' | 'glass' | 'refraction''mask'Built-in card surface mode
surfaceColorstring''Surface base color (theme overlay color by default)
surfaceOpacitynumber0.96Surface opacity (mask mode)
speedBoostnumber1.12Time-scale boost applied after speedBoostAt progress.
speedBoostAtnumber0.7Open/close animation progress threshold that enables speedBoost.
transitionNamestring'TxFlipOverlay-Mask'Vue transition name used for the mask layer.
headerbooleantrueEnable built-in header when no #header slot is provided
headerTitlestring''Built-in header title
headerDescstring''Built-in header description
closablebooleantrueShow built-in round close button
closeAriaLabelstring'Close'Built-in close button aria-label
maskClassstring''Mask class
cardClassstring''Card class
cardStyleCSSProperties-Inline style object forwarded to the overlay card.
border'solid' | 'dashed' | 'dash' | 'none''solid'Card border style (dash aliases dashed)
scrollablebooleantrueWhether body area scrolls internally
expandedboolean-Optional controlled expanded animation state for consumers that need sync telemetry.
animatingboolean-Optional controlled animation state for consumers that need sync telemetry.

Events

EventParamsDescription
update:modelValue(value: boolean)Emitted with false when the overlay closes itself.
open-Open animation starts
opened-Open animation ends
close-Close animation starts
closed-Close animation ends
update:expanded(value: boolean)Sync expanded
update:animating(value: boolean)Sync animating

Slots

SlotParamsDescription
default{ close, expanded, animating, closable, headerTitle, headerDesc }Overlay body content
header{ close, expanded, animating, closable, headerTitle, headerDesc }Full custom header (overrides built-in header system)
header-display{ close, expanded, animating, closable, headerTitle, headerDesc }Custom built-in title/description area
header-actions{ close, expanded, animating, closable, headerTitle, headerDesc }Custom area left of close button
header-close{ close, expanded, animating, closable, headerTitle, headerDesc }Custom close area (hidden when closable=false)

Expose

MethodTypeDescription
close()() => voidRuns the full close animation and emits update:modelValue(false); the parent must own open state via v-model.

Overview

  • modelValue=true mounts the overlay, resolves the source rectangle, emits open, then emits opened after the card animation completes.
  • The overlay teleports to <body>. Its mask and card are position: fixed, and rendered in place they would be laid out against the nearest ancestor with a transform, filter, contain or content-visibility instead of the viewport. Non-prop attributes still land on the mask.
  • Built-in close button, slot close(), mask click, and Escape all start the close path. Mask click and Escape share handleMaskClick, so maskClosable=false blocks both and preventAccidentalClose flashes the warning instead; the close button and close() bypass those gates.
  • Internal close emits close, then update:modelValue(false), then closed. Parent code must update v-model to fully close controlled overlays.
  • Header render priority: #header fully overrides the built-in header; otherwise header=false hides it and header=true renders the built-in header (customizable via #header-display / #header-actions / #header-close). closable=false hides the entire close area, including #header-close.
  • expanded and animating are sync telemetry values; bind them only when the surrounding UI needs to observe overlay motion state.
  • globalMask=true uses a shared body-level mask for stacked overlays; underlay masks are non-interactive and only the top overlay receives mask clicks. Stack displacement is enabled only when adjacent overlays have similar width/height (|delta| <= max(8px, previousSize * 5%)), is capped at depth 3 (-18/-36/-54px with 0.95/0.90/0.85 scale), and deeper layers fade 1.00 → 0.92 → 0.78 → 0.62 → 0.38 → 0.16 → 0.
  • preventAccidentalClose=true blocks mask close and page exit attempts, then flashes the warning state instead of silently closing.
  • Accessibility: the card is role="dialog" with aria-modal="true" and tabindex="-1". When the built-in header renders, headerTitle wires aria-labelledby and headerDesc wires aria-describedby. Focus moves into the card on open and is restored to the previously focused element on close.

Technologies

  • State contract: TxFlipOverlay owns the mount/animation lifecycle but only emits update:modelValue(false) when it closes itself; consumers must still sync v-model after built-in close, slot close(), or mask close.
  • Mount contract: the overlay used to render in place. A docs page's article body is content-visibility: auto, so the "fixed" card centred itself on the whole article, focusing it scrolled the page about 870px, and the trigger it flips from left the screen. It now teleports the way TxModal and TxCommandPalette do; the FlipDialog wrappers that already put it inside <Teleport to="body"> are unaffected.
  • Stacking contract: globalMask=true shares the visual backdrop across open overlays while only the top overlay keeps an interactive mask; size-similar overlays receive capped displacement and deeper overlays fade out.
  • Safety contract: preventAccidentalClose blocks mask close and page-exit attempts, then flashes the warning state; it is a guardrail, not a persistence or autosave feature.
  • Verified coverage: flip-overlay.test.ts covers defaults, header slot priority, close ordering, dialog semantics (role/aria-modal/aria-labelledby) with Escape close, card style forwarding, stacked masks, layered displacement, and blocked-close behavior.
  • Component source: packages/tuffex/packages/components/src/flip-overlay/src/TxFlipOverlay.vue.
  • Motion implementation: packages/tuffex/packages/components/src/flip-overlay/src/flip-overlay-motion.ts.
  • Types: packages/tuffex/packages/components/src/flip-overlay/src/types.ts.
  • Coverage: packages/tuffex/packages/components/src/flip-overlay/__tests__/flip-overlay.test.ts verifies surface defaults, header slot priority, close event order, card style forwarding, stacked masks, layered displacement, and safety-close guards.
查看源码
packages/tuffex/packages/components/src/flip-overlay/index.ts