@ -1,85 +1,13 @@
import { EACH _IS _ANIMATED , EACH _IS _CONTROLLED } from '../../../../constants.js' ;
import { run _all } from '../../../common.js' ;
import {
AWAIT _BLOCK ,
DYNAMIC _COMPONENT _BLOCK ,
EACH _BLOCK ,
EACH _ITEM _BLOCK ,
IF _BLOCK ,
KEY _BLOCK ,
ROOT _BLOCK
} from '../../constants.js' ;
import { destroy _each _item _block , get _first _element } from '../blocks/each.js' ;
import { schedule _raf _task } from '../task.js' ;
import { append _child , empty } from '../operations.js' ;
import {
destroy _effect ,
effect ,
managed _effect ,
managed _pre _effect
} from '../../reactivity/effects.js' ;
import {
current _block ,
current _effect ,
execute _effect ,
mark _subtree _inert ,
untrack
} from '../../runtime.js' ;
import { noop } from '../../../common.js' ;
import { user _effect } from '../../reactivity/effects.js' ;
import { current _effect , untrack } from '../../runtime.js' ;
import { raf } from '../../timing.js' ;
const active _tick _animations = new Set ( ) ;
const DELAY _NEXT _TICK = Number . MIN _SAFE _INTEGER ;
/** @type {undefined | number} */
let active _tick _ref = undefined ;
/ * *
* @ template P
* @ param { HTMLElement } dom
* @ param { ( ) => import ( '#client' ) . TransitionFn < P | undefined > } get _transition _fn
* @ param { ( ( ) => P ) | null } props
* @ param { any } global
* @ returns { void }
* /
export function transition ( dom , get _transition _fn , props , global = false ) {
bind _transition ( dom , get _transition _fn , props , 'both' , global ) ;
}
/ * *
* @ template P
* @ param { HTMLElement } dom
* @ param { ( ) => import ( '#client' ) . TransitionFn < P | undefined > } get _transition _fn
* @ param { ( ( ) => P ) | null } props
* @ returns { void }
* /
export function animate ( dom , get _transition _fn , props ) {
bind _transition ( dom , get _transition _fn , props , 'key' , false ) ;
}
/ * *
* @ template P
* @ param { HTMLElement } dom
* @ param { ( ) => import ( '#client' ) . TransitionFn < P | undefined > } get _transition _fn
* @ param { ( ( ) => P ) | null } props
* @ param { any } global
* @ returns { void }
* /
function in _fn ( dom , get _transition _fn , props , global = false ) {
bind _transition ( dom , get _transition _fn , props , 'in' , global ) ;
}
export { in _fn as in } ;
/ * *
* @ template P
* @ param { HTMLElement } dom
* @ param { ( ) => import ( '#client' ) . TransitionFn < P | undefined > } get _transition _fn
* @ param { ( ( ) => P ) | null } props
* @ param { any } global
* @ returns { void }
* /
export function out ( dom , get _transition _fn , props , global = false ) {
bind _transition ( dom , get _transition _fn , props , 'out' , global ) ;
}
import { loop } from '../../loop.js' ;
import { should _intro } from '../../render.js' ;
import { is _function } from '../../utils.js' ;
import { current _each _item _block } from '../blocks/each.js' ;
import { TRANSITION _GLOBAL , TRANSITION _IN , TRANSITION _OUT } from '../../../../constants.js' ;
import { EFFECT _RAN } from '../../constants.js' ;
/ * *
* @ template T
@ -95,7 +23,7 @@ function custom_event(type, detail, { bubbles = false, cancelable = false } = {}
}
/ * *
* @ param { HTML Element} dom
* @ param { Element} dom
* @ param { 'introstart' | 'introend' | 'outrostart' | 'outroend' } type
* @ returns { void }
* /
@ -137,685 +65,281 @@ function css_to_keyframe(css) {
return keyframe ;
}
class TickAnimation {
/** @type {null | (() => void)} */
onfinish ;
/** @type {(t: number, u: number) => string} */
# tick _fn ;
/** @type {number} */
# duration ;
/** @type {number} */
# current ;
/** @type {number} */
# delay ;
/** @type {number} */
# previous ;
/** @type {boolean} */
paused ;
/** @type {boolean} */
# reversed ;
/** @type {number} */
# delay _current ;
/** @type {boolean} */
# delayed _reverse ;
/** @param {number} t */
const linear = ( t ) => t ;
/ * *
* @ param { ( t : number , u : number ) => string } tick _fn
* @ param { number } duration
* @ param { number } delay
* @ param { boolean } out
* Called inside keyed ` {#each ...} ` blocks ( as ` $ .animation(...) ` ) . This creates an animation manager
* and attaches it to the block , so that moves can be animated following reconciliation .
* @ template P
* @ param { Element } element
* @ param { ( ) => import ( '#client' ) . AnimateFn < P | undefined > } get _fn
* @ param { ( ( ) => P ) | null } get _params
* /
constructor ( tick _fn , duration , delay , out ) {
this . # duration = duration ;
this . # delay = delay ;
this . paused = false ;
this . # tick _fn = tick _fn ;
this . # reversed = out ;
this . # delay _current = delay ;
this . # current = out ? duration : 0 ;
this . # previous = 0 ;
this . # delayed _reverse = false ;
this . onfinish = null ;
if ( this . # delay ) {
if ( ! out ) {
this . # tick _fn ( 0 , 1 ) ;
}
}
}
export function animation ( element , get _fn , get _params ) {
var block = /** @type {import('#client').EachItemBlock} */ ( current _each _item _block ) ;
pause ( ) {
this . paused = true ;
}
/** @type {DOMRect} */
var from ;
play ( ) {
this . paused = false ;
if ( ! active _tick _animations . has ( this ) ) {
this . # previous = raf . now ( ) ;
if ( active _tick _ref === undefined ) {
active _tick _ref = raf . tick ( handle _raf ) ;
}
active _tick _animations . add ( this ) ;
}
}
/** @type {DOMRect} */
var to ;
# reverse ( ) {
this . # reversed = ! this . # reversed ;
if ( this . paused ) {
if ( this . # current === 0 ) {
this . # current = this . # duration ;
}
this . play ( ) ;
}
}
/** @type {import('#client').Animation | undefined} */
var animation ;
reverse ( ) {
if ( this . # delay === 0 ) {
this . # reverse ( ) ;
} else {
this . # delay _current = this . # delay ;
this . # delayed _reverse = true ;
}
}
block . a ? ? = {
element ,
measure ( ) {
from = this . element . getBoundingClientRect ( ) ;
} ,
apply ( ) {
animation ? . abort ( ) ;
cancel ( ) {
active _tick _animations . delete ( this ) ;
const current = this . # current / this . # duration ;
if ( current > 0 && current < 1 ) {
const t = this . # reversed ? 1 : 0 ;
this . # tick _fn ( t , 1 - t ) ;
}
}
to = this . element . getBoundingClientRect ( ) ;
finish ( ) {
active _tick _animations . delete ( this ) ;
if ( this . onfinish ) {
this . onfinish ( ) ;
}
}
const options = get _fn ( ) ( this . element , { from , to } , get _params ? . ( ) ) ;
/** @param {number} time */
_update ( time ) {
let diff = time - this . # previous ;
this . # previous = time ;
if ( this . # delay _current !== 0 ) {
const is _delayed = this . # delay _current === DELAY _NEXT _TICK ;
let cancel = ! this . # delayed _reverse ;
this . # delay _current -= diff ;
if ( this . # delay _current < 0 || is _delayed || ( this . # delay _current === 0 && this . # reversed ) ) {
const delay _diff = is _delayed ? 0 : - this . # delay _current ;
this . # delay _current = 0 ;
if ( this . # delayed _reverse ) {
this . # delayed _reverse = false ;
this . # reverse ( ) ;
} else if ( delay _diff !== 0 || this . # reversed ) {
diff = delay _diff ;
}
cancel = false ;
} else if ( this . # delay _current === 0 ) {
this . # delay _current = DELAY _NEXT _TICK ;
}
if ( cancel ) {
return ;
}
}
this . # current += this . # reversed ? - diff : diff ;
let t = this . # current / this . # duration ;
if ( t < 0 ) {
t = 0 ;
} else if ( t > 1 ) {
t = 1 ;
}
if ( ( this . # reversed && t <= 0 ) || ( ! this . # reversed && t >= 1 ) ) {
t = this . # reversed ? 0 : 1 ;
if ( this . # delay _current === 0 ) {
active _tick _animations . delete ( this ) ;
if ( this . onfinish ) {
this . paused = true ;
this . onfinish ( ) ;
}
}
}
this . # tick _fn ( t , 1 - t ) ;
if (
from . left !== to . left ||
from . right !== to . right ||
from . top !== to . top ||
from . bottom !== to . bottom
) {
animation = animate ( this . element , options , undefined , 1 , ( ) => {
animation ? . abort ( ) ;
animation = undefined ;
} ) ;
}
}
} ;
/** @param {number} time */
function handle _raf ( time ) {
for ( const animation of active _tick _animations ) {
if ( ! animation . paused ) {
animation . _update ( time ) ;
}
}
if ( active _tick _animations . size !== 0 ) {
active _tick _ref = raf . tick ( handle _raf ) ;
} else {
active _tick _ref = undefined ;
}
// in the case of a `<svelte:element>`, it's possible for `$.animation(...)` to be called
// when an animation manager already exists, if the tag changes. in that case, we need to
// swap out the element rather than creating a new manager, in case it happened at the same
// moment as a reconciliation
block . a . element = element ;
}
/ * *
* @ param { { ( t : number ) : number ; ( t : number ) : number ; ( arg0 : number ) : any ; } } easing _fn
* @ param { ( ( t : number , u : number ) => string ) } css _fn
* @ param { number } duration
* @ param { string } direction
* @ param { boolean } reverse
* Called inside block effects as ` $ .transition(...) ` . This creates a transition manager and
* attaches it to the current effect — later , inside ` pause_effect ` and ` resume_effect ` , we
* use this to create ` intro ` and ` outro ` transitions .
* @ template P
* @ param { number } flags
* @ param { HTMLElement } element
* @ param { ( ) => import ( '#client' ) . TransitionFn < P | undefined > } get _fn
* @ param { ( ( ) => P ) | null } get _params
* @ returns { void }
* /
function create _keyframes ( easing _fn , css _fn , duration , direction , reverse ) {
/** @type {Keyframe[]} */
const keyframes = [ ] ;
// We need at least two frames
const frame _time = 16.666 ;
const max _duration = Math . max ( duration , frame _time ) ;
// Have a keyframe every fame for 60 FPS
for ( let i = 0 ; i <= max _duration ; i += frame _time ) {
let time ;
if ( i + frame _time > max _duration ) {
time = 1 ;
} else if ( i === 0 ) {
time = 0 ;
} else {
time = i / max _duration ;
}
let t = easing _fn ( time ) ;
if ( reverse ) {
t = 1 - t ;
}
keyframes . push ( css _to _keyframe ( css _fn ( t , 1 - t ) ) ) ;
}
if ( direction === 'out' || reverse ) {
keyframes . reverse ( ) ;
}
return keyframes ;
}
export function transition ( flags , element , get _fn , get _params ) {
var is _intro = ( flags & TRANSITION _IN ) !== 0 ;
var is _outro = ( flags & TRANSITION _OUT ) !== 0 ;
var is _global = ( flags & TRANSITION _GLOBAL ) !== 0 ;
/** @param {number} t */
const linear = ( t ) => t ;
/** @type {'in' | 'out' | 'both'} */
var direction = is _intro && is _outro ? 'both' : is _intro ? 'in' : 'out' ;
/ * *
* @ param { HTMLElement } dom
* @ param { ( ) => import ( '../../types.js' ) . TransitionPayload } init
* @ param { 'in' | 'out' | 'both' | 'key' } direction
* @ param { import ( '../../types.js' ) . Effect } effect
* @ returns { import ( '../../types.js' ) . Transition }
* /
function create _transition ( dom , init , direction , effect ) {
let curr _direction = 'in' ;
/** @type {import('#client').AnimationConfig | ((opts: { direction: 'in' | 'out' }) => import('#client').AnimationConfig) | undefined} */
var current _options ;
/** @type {Array<() => void>} */
let subs = [ ] ;
var inert = element . inert ;
/** @type {null | Animation | TickAnimation} */
let animation = null ;
let cancelled = false ;
/** @type {import('#client').Animation | undefined} */
var intro ;
const create _animation = ( ) => {
let payload = /** @type {import('../../types.js').TransitionPayload} */ ( transition . p ) ;
if ( typeof payload === 'function' ) {
// @ts-ignore
payload = payload ( { direction : curr _direction } ) ;
}
if ( payload == null ) {
return ;
}
const duration = payload . duration ? ? 300 ;
const delay = payload . delay ? ? 0 ;
const css _fn = payload . css ;
const tick _fn = payload . tick ;
const easing _fn = payload . easing || linear ;
if ( typeof tick _fn === 'function' ) {
animation = new TickAnimation ( tick _fn , duration , delay , direction === 'out' ) ;
} else {
const keyframes =
typeof css _fn === 'function'
? create _keyframes ( easing _fn , css _fn , duration , direction , false )
: [ ] ;
animation = dom . animate ( keyframes , {
duration ,
endDelay : delay ,
delay ,
fill : 'both'
} ) ;
}
animation . pause ( ) ;
/** @type {import('#client').Animation | undefined} */
var outro ;
animation . onfinish = ( ) => {
const is _outro = curr _direction === 'out' ;
/** @type {Animation | TickAnimation} */ ( animation ) . cancel ( ) ;
if ( is _outro ) {
run _all ( subs ) ;
subs = [ ] ;
/** @type {(() => void) | undefined} */
var reset ;
function get _options ( ) {
// If a transition is still ongoing, we use the existing options rather than generating
// new ones. This ensures that reversible transitions reverse smoothly, rather than
// jumping to a new spot because (for example) a different `duration` was used
return ( current _options ? ? = get _fn ( ) ( element , get _params ? . ( ) , { direction } ) ) ;
}
dispatch _event ( dom , is _outro ? 'outroend' : 'introend' ) ;
} ;
} ;
/** @type {import('../../types.js').Transition} */
const transition = {
e : effect ,
i : init ,
// payload
p : null ,
// finished
/** @param {() => void} fn */
f ( fn ) {
subs . push ( fn ) ;
} ,
/** @type {import('#client').TransitionManager} */
var transition = {
is _global ,
in ( ) {
const needs _reverse = curr _direction !== 'in' ;
curr _direction = 'in' ;
if ( animation === null || cancelled ) {
cancelled = false ;
create_animation ( ) ;
}
if ( animation === null ) {
transition . x ( ) ;
element . inert = inert ;
if ( is _intro ) {
dispatch _event ( element , 'introstart' ) ;
intro = animate ( element , get _options ( ) , outro , 1 , ( ) => {
dispatch _event ( element , 'introend' ) ;
intro = current _options = undefined ;
} ) ;
} else {
dispatch _event ( dom , 'introstart' ) ;
if ( needs _reverse ) {
/** @type {Animation | TickAnimation} */ ( animation ) . reverse ( ) ;
}
/** @type {Animation | TickAnimation} */ ( animation ) . play ( ) ;
outro ? . abort ( ) ;
reset ? . ( ) ;
}
} ,
// out
o ( ) {
// @ts-ignore
const has _keyed _transition = dom . _ _animate ;
// If we're outroing an element that has an animation, then we need to fix
// its position to ensure it behaves nicely without causing layout shift.
if ( has _keyed _transition ) {
const style = getComputedStyle ( dom ) ;
const position = style . position ;
if ( position !== 'absolute' && position !== 'fixed' ) {
const { width , height } = style ;
const a = dom . getBoundingClientRect ( ) ;
dom . style . position = 'absolute' ;
dom . style . width = width ;
dom . style . height = height ;
const b = dom . getBoundingClientRect ( ) ;
if ( a . left !== b . left || a . top !== b . top ) {
const translate = ` translate( ${ a . left - b . left } px, ${ a . top - b . top } px) ` ;
const existing _transform = style . transform ;
if ( existing _transform === 'none' ) {
dom . style . transform = translate ;
} else {
// Previously, in the Svelte 4, we'd just apply the transform the the DOM element. However,
// because we're now using Web Animations, we can't do that as it won't work properly if the
// animation is also making use of the same transformations. So instead, we apply an
// instantaneous animation and pause it on the first frame, just applying the same behavior.
// We also need to take into consideration matrix transforms and how they might combine with
// an existing behavior that is already in progress (such as scale).
// > Follow the white rabbit.
const transform = existing _transform . startsWith ( 'matrix(1,' )
? translate
: ` matrix(1,0,0,1,0,0) ` ;
const frame = {
transform
} ;
const animation = dom . animate ( [ frame , frame ] , { duration : 1 } ) ;
animation . pause ( ) ;
}
}
}
}
const needs _reverse = direction === 'both' && curr _direction !== 'out' ;
curr _direction = 'out' ;
if ( animation === null || cancelled ) {
cancelled = false ;
create _animation ( ) ;
}
if ( animation === null ) {
transition . x ( ) ;
} else {
dispatch _event ( dom , 'outrostart' ) ;
if ( needs _reverse ) {
const payload = transition . p ;
const current _animation = /** @type {Animation} */ ( animation ) ;
// If we are working with CSS animations, then before we call reverse, we also need to ensure
// that we reverse the easing logic. To do this we need to re-create the keyframes so they're
// in reverse with easing properly reversed too.
if (
payload !== null &&
payload . css !== undefined &&
current _animation . playState === 'idle'
) {
const duration = payload . duration ? ? 300 ;
const css _fn = payload . css ;
const easing _fn = payload . easing || linear ;
const keyframes = create _keyframes ( easing _fn , css _fn , duration , direction , true ) ;
const effect = current _animation . effect ;
if ( effect !== null ) {
// @ts-ignore
effect . setKeyframes ( keyframes ) ;
}
}
/** @type {Animation | TickAnimation} */ ( animation ) . reverse ( ) ;
out ( fn ) {
if ( is _outro ) {
element . inert = true ;
dispatch _event ( element , 'outrostart' ) ;
outro = animate ( element , get _options ( ) , intro , 0 , ( ) => {
dispatch _event ( element , 'outroend' ) ;
outro = current _options = undefined ;
fn ? . ( ) ;
} ) ;
// TODO arguably the outro should never null itself out until _all_ outros for this effect have completed...
// in that case we wouldn't need to store `reset` separately
reset = outro . reset ;
} else {
/** @type {Animation | TickAnimation} */ ( animation ) . play ( ) ;
}
fn ? . ( ) ;
}
} ,
// cancel
c ( ) {
if ( animation !== null ) {
/** @type {Animation | TickAnimation} */ ( animation ) . cancel ( ) ;
stop : ( ) => {
intro ? . abort ( ) ;
outro ? . abort ( ) ;
}
cancelled = true ;
} ,
// cleanup
x ( ) {
run _all ( subs ) ;
subs = [ ] ;
} ,
r : direction ,
d : dom
} ;
return transition ;
}
/ * *
* @ param { import ( '../../types.js' ) . Block } block
* @ returns { boolean }
* /
function is _transition _block ( block ) {
const type = block . t ;
return (
type === IF _BLOCK ||
type === EACH _ITEM _BLOCK ||
type === KEY _BLOCK ||
type === AWAIT _BLOCK ||
type === DYNAMIC _COMPONENT _BLOCK ||
( type === EACH _BLOCK && block . v . length === 0 )
) ;
}
var effect = /** @type {import('#client').Effect} */ ( current _effect ) ;
/ * *
* @ template P
* @ param { HTMLElement } dom
* @ param { ( ) => import ( '../../types.js' ) . TransitionFn < P | undefined > | import ( '../../types.js' ) . AnimateFn < P | undefined > } get _transition _fn
* @ param { ( ( ) => P ) | null } props _fn
* @ param { 'in' | 'out' | 'both' | 'key' } direction
* @ param { boolean } global
* @ returns { void }
* /
function bind _transition ( dom , get _transition _fn , props _fn , direction , global ) {
const transition _effect = /** @type {import('../../types.js').Effect} */ ( current _effect ) ;
const block = current _block ;
const is _keyed _transition = direction === 'key' ;
( effect . transitions ? ? = [ ] ) . push ( transition ) ;
let can _show _intro _on _mount = true ;
let can _apply _lazy _transitions = false ;
// if this is a local transition, we only want to run it if the parent (block) effect's
// parent (branch) effect is where the state change happened. we can determine that by
// looking at whether the branch effect is currently initializing
if ( is _intro && should _intro ) {
var parent = /** @type {import('#client').Effect} */ ( effect . parent ) ;
if ( is _keyed _transition ) {
// @ts-ignore
dom . _ _animate = true ;
}
/** @type {import('../../types.js').Block | null} */
let transition _block = block ;
main : while ( transition _block !== null ) {
if ( is _transition _block ( transition _block ) ) {
if ( transition _block . t === EACH _ITEM _BLOCK ) {
// Lazily apply the each block transition
transition _block . r = each _item _transition ;
transition _block . a = each _item _animate ;
transition _block = transition _block . p ;
} else if ( transition _block . t === AWAIT _BLOCK && transition _block . n /* pending */ ) {
can _show _intro _on _mount = true ;
} else if ( transition _block . t === IF _BLOCK ) {
transition _block . r = if _block _transition ;
if ( can _show _intro _on _mount ) {
/** @type {import('../../types.js').Block | null} */
let if _block = transition _block ;
while ( if _block . t === IF _BLOCK ) {
// If we have an if block parent that is currently falsy then
// we can show the intro on mount as long as that block is mounted
if ( if _block . e !== null && ! if _block . v ) {
can _show _intro _on _mount = true ;
break main ;
}
if _block = if _block . p ;
}
}
}
if ( ! can _apply _lazy _transitions && can _show _intro _on _mount ) {
can _show _intro _on _mount = transition _block . e !== null ;
}
if ( can _show _intro _on _mount || ! global ) {
can _apply _lazy _transitions = true ;
if ( is _global || ( parent . f & EFFECT _RAN ) !== 0 ) {
user _effect ( ( ) => {
untrack ( ( ) => transition . in ( ) ) ;
} ) ;
}
} else if ( transition _block . t === ROOT _BLOCK && ! can _apply _lazy _transitions ) {
can _show _intro _on _mount = transition _block . e !== null || transition _block . i ;
}
transition _block = transition _block . p ;
}
/** @type {import('../../types.js').Transition} */
let transition ;
/ * *
* Animates an element , according to the provided configuration
* @ param { Element } element
* @ param { import ( '#client' ) . AnimationConfig | ( ( opts : { direction : 'in' | 'out' } ) => import ( '#client' ) . AnimationConfig ) } options
* @ param { import ( '#client' ) . Animation | undefined } counterpart The corresponding intro / outro to this outro / intro
* @ param { number } t2 The target ` t ` value — ` 1 ` for intro , ` 0 ` for outro
* @ param { ( ( ) => void ) | undefined } callback
* @ returns { import ( '#client' ) . Animation }
* /
function animate ( element , options , counterpart , t2 , callback ) {
if ( is _function ( options ) ) {
// In the case of a deferred transition (such as `crossfade`), `option` will be
// a function rather than an `AnimationConfig`. We need to call this function
// once DOM has been updated...
/** @type {import('#client').Animation} */
var a ;
user _effect ( ( ) => {
var o = untrack ( ( ) => options ( { direction : t2 === 1 ? 'in' : 'out' } ) ) ;
a = animate ( element , o , counterpart , t2 , callback ) ;
} ) ;
effect ( ( ) => {
let already _mounted = false ;
if ( transition !== undefined ) {
already _mounted = true ;
// Destroy any existing transitions first
transition . x ( ) ;
// ...but we want to do so without using `async`/`await` everywhere, so
// we return a facade that allows everything to remain synchronous
return {
abort : ( ) => a . abort ( ) ,
deactivate : ( ) => a . deactivate ( ) ,
reset : ( ) => a . reset ( ) ,
t : ( now ) => a . t ( now )
} ;
}
const transition _fn = get _transition _fn ( ) ;
/** @param {DOMRect} [from] */
const init = ( from ) =>
untrack ( ( ) => {
const props = props _fn === null ? { } : props _fn ( ) ;
return is _keyed _transition
? /** @type {import('../../types.js').AnimateFn<any>} */ ( transition _fn ) (
dom ,
{ from : /** @type {DOMRect} */ ( from ) , to : dom . getBoundingClientRect ( ) } ,
props ,
{ }
)
: /** @type {import('../../types.js').TransitionFn<any>} */ ( transition _fn ) ( dom , props , {
direction
} ) ;
} ) ;
transition = create _transition ( dom , init , direction , transition _effect ) ;
const is _intro = direction === 'in' ;
const show _intro = can _show _intro _on _mount && ( is _intro || direction === 'both' ) ;
counterpart ? . deactivate ( ) ;
if ( show _intro && ! already _mounted ) {
transition . p = transition . i ( ) ;
if ( ! options ? . duration ) {
callback ? . ( ) ;
return {
abort : noop ,
deactivate : noop ,
reset : noop ,
t : ( ) => t2
} ;
}
const effect = managed _pre _effect ( ( ) => {
destroy _effect ( effect ) ;
dom . inert = false ;
var { delay = 0 , duration , css , tick , easing = linear } = options ;
if ( show _intro && ! already _mounted ) {
transition . in ( ) ;
}
var start = raf . now ( ) + delay ;
var t1 = counterpart ? . t ( start ) ? ? 1 - t2 ;
var delta = t2 - t1 ;
/** @type {import('../../types.js').Block | null} */
let transition _block = block ;
while ( ! is _intro && transition _block !== null ) {
const parent = transition _block . p ;
if ( is _transition _block ( transition _block ) ) {
if ( transition _block . r !== null ) {
transition _block . r ( transition ) ;
}
if (
parent === null ||
( ! global && ( transition _block . t !== IF _BLOCK || parent . t !== IF _BLOCK || parent . v ) )
) {
break ;
}
}
transition _block = parent ;
}
} ) ;
} ) ;
duration *= Math . abs ( delta ) ;
var end = start + duration ;
if ( direction === 'key' ) {
effect ( ( ) => {
return ( ) => {
transition . x ( ) ;
} ;
} ) ;
}
}
/** @type {Animation} */
var animation ;
/ * *
* @ param { Set < import ( '../../types.js' ) . Transition > } transitions
* @ param { 'in' | 'out' | 'key' } target _direction
* @ param { DOMRect } [ from ]
* @ returns { void }
* /
export function trigger _transitions ( transitions , target _direction , from ) {
/** @type {Array<() => void>} */
const outros = [ ] ;
for ( const transition of transitions ) {
const direction = transition . r ;
const effect = transition . e ;
if ( target _direction === 'in' ) {
if ( direction === 'in' || direction === 'both' ) {
transition . in ( ) ;
} else {
transition . c ( ) ;
}
transition . d . inert = false ;
mark _subtree _inert ( effect , false ) ;
} else if ( target _direction === 'key' ) {
if ( direction === 'key' ) {
if ( ! transition . p ) {
transition . p = transition . i ( /** @type {DOMRect} */ ( from ) ) ;
}
transition . in ( ) ;
}
} else {
if ( direction === 'out' || direction === 'both' ) {
if ( ! transition . p ) {
transition . p = transition . i ( ) ;
}
outros . push ( transition . o ) ;
}
transition . d . inert = true ;
mark _subtree _inert ( effect , true ) ;
}
}
if ( outros . length > 0 ) {
// Defer the outros to a microtask
const e = managed _pre _effect ( ( ) => {
destroy _effect ( e ) ;
const e2 = managed _effect ( ( ) => {
destroy _effect ( e2 ) ;
run _all ( outros ) ;
} ) ;
} ) ;
}
}
/** @type {import('#client').Task} */
var task ;
/ * *
* @ this { import ( '../../types.js' ) . IfBlock }
* @ param { import ( '../../types.js' ) . Transition } transition
* @ returns { void }
* /
function if _block _transition ( transition ) {
const block = this ;
// block.value === true
if ( block . v ) {
const consequent _transitions = ( block . c ? ? = new Set ( ) ) ;
consequent _transitions . add ( transition ) ;
transition . f ( ( ) => {
const c = /** @type {Set<import('../../types.js').Transition>} */ ( consequent _transitions ) ;
c . delete ( transition ) ;
// If the block has changed to falsy and has transitions
if ( ! block . v && c . size === 0 ) {
const consequent _effect = block . ce ;
execute _effect ( /** @type {import('../../types.js').Effect} */ ( consequent _effect ) ) ;
if ( css ) {
// WAAPI
var keyframes = [ ] ;
var n = duration / ( 1000 / 60 ) ;
for ( var i = 0 ; i <= n ; i += 1 ) {
var t = t1 + delta * easing ( i / n ) ;
var styles = css ( t , 1 - t ) ;
keyframes . push ( css _to _keyframe ( styles ) ) ;
}
animation = element . animate ( keyframes , {
delay ,
duration ,
easing : 'linear' ,
fill : 'forwards'
} ) ;
animation . finished
. then ( ( ) => {
callback ? . ( ) ;
} )
. catch ( noop ) ;
} else {
const alternate _transitions = ( block . a ? ? = new Set ( ) ) ;
alternate _transitions . add ( transition ) ;
transition . f ( ( ) => {
const a = /** @type {Set<import('../../types.js').Transition>} */ ( alternate _transitions ) ;
a . delete ( transition ) ;
// If the block has changed to truthy and has transitions
if ( block . v && a . size === 0 ) {
const alternate _effect = block . ae ;
execute _effect ( /** @type {import('../../types.js').Effect} */ ( alternate _effect ) ) ;
}
} ) ;
}
// Timer
if ( t1 === 0 ) {
tick ? . ( 0 , 1 ) ; // TODO put in nested effect, to avoid interleaved reads/writes?
}
/ * *
* @ this { import ( '../../types.js' ) . EachItemBlock }
* @ param { import ( '../../types.js' ) . Transition } transition
* @ returns { void }
* /
function each _item _transition ( transition ) {
const block = this ;
const each _block = block . p ;
const is _controlled = ( each _block . f & EACH _IS _CONTROLLED ) !== 0 ;
// Disable optimization
if ( is _controlled ) {
const anchor = empty ( ) ;
each _block . f ^= EACH _IS _CONTROLLED ;
append _child ( /** @type {Element} */ ( each _block . a ) , anchor ) ;
each _block . a = anchor ;
}
if ( transition . r === 'key' && ( each _block . f & EACH _IS _ANIMATED ) === 0 ) {
each _block . f |= EACH _IS _ANIMATED ;
}
const transitions = ( block . s ? ? = new Set ( ) ) ;
transition . f ( ( ) => {
transitions . delete ( transition ) ;
if ( transition . r !== 'key' ) {
for ( let other of transitions ) {
const type = other . r ;
if ( type === 'key' || type === 'in' ) {
transitions . delete ( other ) ;
}
}
if ( transitions . size === 0 ) {
block . s = null ;
destroy _each _item _block ( block , null , true ) ;
task = loop ( ( now ) => {
if ( now >= end ) {
tick ? . ( t2 , 1 - t2 ) ;
callback ? . ( ) ;
return false ;
}
if ( now >= start ) {
var p = t1 + delta * easing ( ( now - start ) / duration ) ;
tick ? . ( p , 1 - p ) ;
}
return true ;
} ) ;
transitions . add ( transition ) ;
}
/ * *
*
* @ param { import ( '../../types.js' ) . EachItemBlock } block
* @ param { Set < import ( '../../types.js' ) . Transition > } transitions
* /
function each _item _animate ( block , transitions ) {
const from _dom = /** @type {Element} */ ( get _first _element ( block ) ) ;
const from = from _dom . getBoundingClientRect ( ) ;
// Cancel any existing key transitions
for ( const transition of transitions ) {
const type = transition . r ;
if ( type === 'key' ) {
transition . c ( ) ;
return {
abort : ( ) => {
animation ? . cancel ( ) ;
task ? . abort ( ) ;
} ,
deactivate : ( ) => {
callback = undefined ;
} ,
reset : ( ) => {
if ( t2 === 0 ) {
tick ? . ( 1 , 0 ) ;
}
} ,
t : ( now ) => {
var t = t1 + delta * easing ( ( now - start ) / duration ) ;
return Math . min ( 1 , Math . max ( 0 , t ) ) ;
}
schedule _raf _task ( ( ) => {
trigger _transitions ( transitions , 'key' , from ) ;
} ) ;
} ;
}