@ -101,7 +101,7 @@ export default class IfBlock extends Node {
? block . getUniqueName ( ` ${ name } _anchor ` )
: ( this . next && this . next . var ) || 'null' ;
const branches = getBranches ( this . generator , block , parentNode , parentNodes , this ) ;
const branches = this . getBranches ( block , parentNode , parentNodes , this ) ;
const hasElse = isElseBranch ( branches [ branches . length - 1 ] ) ;
const if_name = hasElse ? '' : ` if ( ${ name } ) ` ;
@ -113,21 +113,12 @@ export default class IfBlock extends Node {
if ( this . else ) {
if ( hasOutros ) {
compoundWithOutros (
this . generator ,
block ,
parentNode ,
parentNodes ,
this ,
branches ,
dynamic ,
vars
) ;
this . buildCompoundWithOutros ( block , parentNode , parentNodes , branches , dynamic , vars ) ;
} else {
compound ( this . generator , block , parentNode , parentNode s, thi s, branches , dynamic , vars ) ;
this . buildCompound ( block , parentNode , parentNodes , branches , dynamic , vars ) ;
}
} else {
simple ( this . generator , block , parentNode , parentNode s, thi s, branches [ 0 ] , dynamic , vars ) ;
this . buildSimple ( block , parentNode , parentNodes , branches [ 0 ] , dynamic , vars ) ;
}
block . builders . create . addLine ( ` ${ if_name } ${ name } .c(); ` ) ;
@ -147,353 +138,334 @@ export default class IfBlock extends Node {
) ;
}
}
}
buildCompound (
block : Block ,
parentNode : string ,
parentNodes : string ,
branches ,
dynamic ,
{ name , anchor , hasElse , if_name }
) {
const select_block_type = this . generator . getUniqueName ( ` select_block_type ` ) ;
const current_block_type = block . getUniqueName ( ` current_block_type ` ) ;
const current_block_type_and = hasElse ? '' : ` ${ current_block_type } && ` ;
block . builders . init . addBlock ( deindent `
function $ { select_block_type } ( state ) {
$ { branches
. map ( ( { condition , block } ) = > ` ${ condition ? ` if ( ${ condition } ) ` : '' } return ${ block } ; ` )
. join ( '\n' ) }
}
` );
block . builders . init . addBlock ( deindent `
var $ { current_block_type } = $ { select_block_type } ( state ) ;
var $ { name } = $ { current_block_type_and } $ { current_block_type } ( # component , state ) ;
` );
const mountOrIntro = branches [ 0 ] . hasIntroMethod ? 'i' : 'm' ;
// TODO move all this into the class
const initialMountNode = parentNode || '#target' ;
const anchorNode = parentNode ? 'null' : 'anchor' ;
block . builders . mount . addLine (
` ${ if_name } ${ name } . ${ mountOrIntro } ( ${ initialMountNode } , ${ anchorNode } ); `
) ;
function getBranches (
generator : DomGenerator ,
block : Block ,
parentNode : string ,
parentNodes : string ,
node : Node
) {
block . contextualise ( node . expression ) ; // TODO remove
const updateMountNode = this . getUpdateMountNode ( anchor ) ;
const branches = [
{
condition : node.metadata.snippet ,
block : node.block.name ,
hasUpdateMethod : node.block.hasUpdateMethod ,
hasIntroMethod : node.block.hasIntroMethod ,
hasOutroMethod : node.block.hasOutroMethod ,
} ,
] ;
const changeBlock = deindent `
$ { hasElse
? deindent `
$ { name } . u ( ) ;
$ { name } . d ( ) ;
`
: deindent `
if ( $ { name } ) {
$ { name } . u ( ) ;
$ { name } . d ( ) ;
} ` }
$ { name } = $ { current_block_type_and } $ { current_block_type } ( # component , state ) ;
$ { if_name } $ { name } . c ( ) ;
$ { if_name } $ { name } . $ { mountOrIntro } ( $ { updateMountNode } , $ { anchor } ) ;
` ;
visitChildren ( generator , block , node ) ;
if ( dynamic ) {
block . builders . update . addBlock ( deindent `
if ( $ { current_block_type } === ( $ { current_block_type } = $ { select_block_type } ( state ) ) && $ { name } ) {
$ { name } . p ( changed , state ) ;
} else {
$ { changeBlock }
}
` );
} else {
block . builders . update . addBlock ( deindent `
if ( $ { current_block_type } !== ( $ { current_block_type } = $ { select_block_type } ( state ) ) ) {
$ { changeBlock }
}
` );
}
if ( isElseIf ( node . else ) ) {
branches . push (
. . . getBranches ( generator , block , parentNode , parentNodes , node . else . children [ 0 ] )
) ;
} else {
branches . push ( {
condition : null ,
block : node.else ? node.else.block.name : null ,
hasUpdateMethod : node.else ? node.else.block.hasUpdateMethod : false ,
hasIntroMethod : node.else ? node.else.block.hasIntroMethod : false ,
hasOutroMethod : node.else ? node.else.block.hasOutroMethod : false ,
} ) ;
block . builders . unmount . addLine ( ` ${ if_name } ${ name } .u(); ` ) ;
if ( node . else ) {
visitChildren ( generator , block , node . else ) ;
}
block . builders . destroy . addLine ( ` ${ if_name } ${ name } .d(); ` ) ;
}
return branches ;
}
// if any of the siblings have outros, we need to keep references to the blocks
// (TODO does this only apply to bidi transitions?)
buildCompoundWithOutros (
block : Block ,
parentNode : string ,
parentNodes : string ,
branches ,
dynamic ,
{ name , anchor , hasElse }
) {
const select_block_type = block . getUniqueName ( ` select_block_type ` ) ;
const current_block_type_index = block . getUniqueName ( ` current_block_type_index ` ) ;
const previous_block_index = block . getUniqueName ( ` previous_block_index ` ) ;
const if_block_creators = block . getUniqueName ( ` if_block_creators ` ) ;
const if_blocks = block . getUniqueName ( ` if_blocks ` ) ;
function visitChildren (
generator : DomGenerator ,
block : Block ,
node : Node
) {
node . children . forEach ( ( child : Node ) = > {
child . build ( node . block , null , 'nodes' ) ;
} ) ;
}
const if_current_block_type_index = hasElse
? ''
: ` if (~ ${ current_block_type_index } ) ` ;
block . addVariable ( current_block_type_index ) ;
block . addVariable ( name ) ;
block . builders . init . addBlock ( deindent `
var $ { if_block_creators } = [
$ { branches . map ( branch = > branch . block ) . join ( ',\n' ) }
] ;
function simple (
generator : DomGenerator ,
block : Block ,
parentNode : string ,
parentNodes : string ,
node : Node ,
branch ,
dynamic ,
{ name , anchor , if_name }
) {
block . builders . init . addBlock ( deindent `
var $ { name } = ( $ { branch . condition } ) && $ { branch . block } ( # component , state ) ;
` );
var $ { if_blocks } = [ ] ;
const mountOrIntro = branch . hasIntroMethod ? 'i' : 'm' ;
const initialMountNode = parentNode || '#target' ;
const anchorNode = parentNode ? 'null' : 'anchor' ;
function $ { select_block_type } ( state ) {
$ { branches
. map ( ( { condition , block } , i ) = > ` ${ condition ? ` if ( ${ condition } ) ` : '' } return ${ block ? i : - 1 } ; ` )
. join ( '\n' ) }
}
` );
block . builders . mount . addLine (
` if ( ${ name } ) ${ name } . ${ mountOrIntro } ( ${ initialMountNode } , ${ anchorNode } ); `
) ;
if ( hasElse ) {
block . builders . init . addBlock ( deindent `
$ { current_block_type_index } = $ { select_block_type } ( state ) ;
$ { name } = $ { if_blocks } [ $ { current_block_type_index } ] = $ { if_block_creators } [ $ { current_block_type_index } ] ( # component , state ) ;
` );
} else {
block . builders . init . addBlock ( deindent `
if ( ~ ( $ { current_block_type_index } = $ { select_block_type } ( state ) ) ) {
$ { name } = $ { if_blocks } [ $ { current_block_type_index } ] = $ { if_block_creators } [ $ { current_block_type_index } ] ( # component , state ) ;
}
` );
}
const updateMountNode = node . getUpdateMountNode ( anchor ) ;
const mountOrIntro = branches [ 0 ] . hasIntroMethod ? 'i' : 'm' ;
const initialMountNode = parentNode || '#target' ;
const anchorNode = parentNode ? 'null' : 'anchor' ;
const enter = dynamic
? branch . hasIntroMethod
block . builders . mount . addLine (
` ${ if_current_block_type_index } ${ if_blocks } [ ${ current_block_type_index } ]. ${ mountOrIntro } ( ${ initialMountNode } , ${ anchorNode } ); `
) ;
const updateMountNode = this . getUpdateMountNode ( anchor ) ;
const destroyOldBlock = deindent `
$ { name } . o ( function ( ) {
$ { if_blocks } [ $ { previous_block_index } ] . u ( ) ;
$ { if_blocks } [ $ { previous_block_index } ] . d ( ) ;
$ { if_blocks } [ $ { previous_block_index } ] = null ;
} ) ;
` ;
const createNewBlock = deindent `
$ { name } = $ { if_blocks } [ $ { current_block_type_index } ] ;
if ( ! $ { name } ) {
$ { name } = $ { if_blocks } [ $ { current_block_type_index } ] = $ { if_block_creators } [ $ { current_block_type_index } ] ( # component , state ) ;
$ { name } . c ( ) ;
}
$ { name } . $ { mountOrIntro } ( $ { updateMountNode } , $ { anchor } ) ;
` ;
const changeBlock = hasElse
? deindent `
if ( $ { name } ) {
$ { name } . p ( changed , state ) ;
} else {
$ { name } = $ { branch . block } ( # component , state ) ;
if ( $ { name } ) $ { name } . c ( ) ;
}
$ { destroyOldBlock }
$ { name } . i ( $ { updateMountNode } , $ { anchor } ) ;
$ { createNewBlock }
`
: deindent `
if ( $ { name } ) {
$ { name } . p ( changed , state ) ;
} else {
$ { name } = $ { branch . block } ( # component , state ) ;
$ { name } . c ( ) ;
$ { name } . m ( $ { updateMountNode } , $ { anchor } ) ;
$ { destroyOldBlock }
}
`
: branch . hasIntroMethod
? deindent `
if ( ! $ { name } ) {
$ { name } = $ { branch . block } ( # component , state ) ;
$ { name } . c ( ) ;
}
$ { name } . i ( $ { updateMountNode } , $ { anchor } ) ;
`
: deindent `
if ( ! $ { name } ) {
$ { name } = $ { branch . block } ( # component , state ) ;
$ { name } . c ( ) ;
$ { name } . m ( $ { updateMountNode } , $ { anchor } ) ;
if ( ~ $ { current_block_type_index } ) {
$ { createNewBlock }
} else {
$ { name } = null ;
}
` ;
// no `p()` here — we don't want to update outroing nodes,
// as that will typically result in glitching
const exit = branch . hasOutroMethod
? deindent `
$ { name } . o ( function ( ) {
$ { name } . u ( ) ;
$ { name } . d ( ) ;
$ { name } = null ;
} ) ;
`
: deindent `
$ { name } . u ( ) ;
$ { name } . d ( ) ;
$ { name } = null ;
` ;
block . builders . update . addBlock ( deindent `
if ( $ { branch . condition } ) {
$ { enter }
} else if ( $ { name } ) {
$ { exit }
if ( dynamic ) {
block . builders . update . addBlock ( deindent `
var $ { previous_block_index } = $ { current_block_type_index } ;
$ { current_block_type_index } = $ { select_block_type } ( state ) ;
if ( $ { current_block_type_index } === $ { previous_block_index } ) {
$ { if_current_block_type_index } $ { if_blocks } [ $ { current_block_type_index } ] . p ( changed , state ) ;
} else {
$ { changeBlock }
}
` );
} else {
block . builders . update . addBlock ( deindent `
var $ { previous_block_index } = $ { current_block_type_index } ;
$ { current_block_type_index } = $ { select_block_type } ( state ) ;
if ( $ { current_block_type_index } !== $ { previous_block_index } ) {
$ { changeBlock }
}
` );
}
` );
block . builders . unmount . addLine ( ` ${ if_name } ${ name } .u(); ` ) ;
block . builders . destroy . addLine ( ` ${ if_name } ${ name } .d(); ` ) ;
}
block . builders . destroy . addLine ( deindent `
$ { if_current_block_type_index } {
$ { if_blocks } [ $ { current_block_type_index } ] . u ( ) ;
$ { if_blocks } [ $ { current_block_type_index } ] . d ( ) ;
}
` );
}
function compound (
generator : DomGenerator ,
block : Block ,
parentNode : string ,
buildSimple (
block : Block ,
parentNode : string ,
parentNodes : string ,
node : Node ,
branches ,
dynamic ,
{ name , anchor , hasElse , if_name }
) {
const select_block_type = generator . getUniqueName ( ` select_block_type ` ) ;
const current_block_type = block . getUniqueName ( ` current_block_type ` ) ;
const current_block_type_and = hasElse ? '' : ` ${ current_block_type } && ` ;
generator . blocks . push ( deindent `
function $ { select_block_type } ( state ) {
$ { branches
. map ( ( { condition , block } ) = > ` ${ condition ? ` if ( ${ condition } ) ` : '' } return ${ block } ; ` )
. join ( '\n' ) }
}
` );
block . builders . init . addBlock ( deindent `
var $ { current_block_type } = $ { select_block_type } ( state ) ;
var $ { name } = $ { current_block_type_and } $ { current_block_type } ( # component , state ) ;
` );
const mountOrIntro = branches [ 0 ] . hasIntroMethod ? 'i' : 'm' ;
branch ,
dynamic ,
{ name , anchor , if_name }
) {
block . builders . init . addBlock ( deindent `
var $ { name } = ( $ { branch . condition } ) && $ { branch . block } ( # component , state ) ;
` );
const initialMountNode = parentNode || '#target' ;
const anchorNode = parentNode ? 'null' : 'anchor' ;
block . builders . mount . addLine (
` ${ if_name } ${ name } . ${ mountOrIntro } ( ${ initialMountNode } , ${ anchorNode } ); `
) ;
const mountOrIntro = branch . hasIntroMethod ? 'i' : 'm' ;
const initialMountNode = parentNode || '#target' ;
const anchorNode = parentNode ? 'null' : 'anchor' ;
const updateMountNode = node . getUpdateMountNode ( anchor ) ;
block . builders . mount . addLine (
` if ( ${ name } ) ${ name } . ${ mountOrIntro } ( ${ initialMountNode } , ${ anchorNode } ); `
) ;
const changeBlock = deindent `
$ { hasElse
const updateMountNode = this . getUpdateMountNode ( anchor ) ;
const enter = dynamic
? branch . hasIntroMethod
? deindent `
if ( $ { name } ) {
$ { name } . p ( changed , state ) ;
} else {
$ { name } = $ { branch . block } ( # component , state ) ;
if ( $ { name } ) $ { name } . c ( ) ;
}
$ { name } . i ( $ { updateMountNode } , $ { anchor } ) ;
`
: deindent `
if ( $ { name } ) {
$ { name } . p ( changed , state ) ;
} else {
$ { name } = $ { branch . block } ( # component , state ) ;
$ { name } . c ( ) ;
$ { name } . m ( $ { updateMountNode } , $ { anchor } ) ;
}
`
: branch . hasIntroMethod
? deindent `
if ( ! $ { name } ) {
$ { name } = $ { branch . block } ( # component , state ) ;
$ { name } . c ( ) ;
}
$ { name } . i ( $ { updateMountNode } , $ { anchor } ) ;
`
: deindent `
if ( ! $ { name } ) {
$ { name } = $ { branch . block } ( # component , state ) ;
$ { name } . c ( ) ;
$ { name } . m ( $ { updateMountNode } , $ { anchor } ) ;
}
` ;
// no `p()` here — we don't want to update outroing nodes,
// as that will typically result in glitching
const exit = branch . hasOutroMethod
? deindent `
$ { name } . u ( ) ;
$ { name } . d ( ) ;
`
: deindent `
if ( $ { name } ) {
$ { name } . o ( function ( ) {
$ { name } . u ( ) ;
$ { name } . d ( ) ;
} ` }
$ { name } = $ { current_block_type_and } $ { current_block_type } ( # component , state ) ;
$ { if_name } $ { name } . c ( ) ;
$ { if_name } $ { name } . $ { mountOrIntro } ( $ { updateMountNode } , $ { anchor } ) ;
` ;
$ { name } = null ;
} ) ;
`
: deindent `
$ { name } . u ( ) ;
$ { name } . d ( ) ;
$ { name } = null ;
` ;
if ( dynamic ) {
block . builders . update . addBlock ( deindent `
if ( $ { current_block_type } === ( $ { current_block_type } = $ { select_block_type } ( state ) ) && $ { name } ) {
$ { name } . p ( changed , state ) ;
} else {
$ { changeBlock }
if ( $ { branch. condition } ) {
$ { enter}
} else if ( $ { name } ) {
$ { exit }
}
` );
} else {
block . builders . update . addBlock ( deindent `
if ( $ { current_block_type } !== ( $ { current_block_type } = $ { select_block_type } ( state ) ) ) {
$ { changeBlock }
}
` );
}
block . builders . unmount . addLine ( ` ${ if_name } ${ name } .u(); ` ) ;
block . builders . unmount . addLine ( ` ${ if_name } ${ name } .u(); ` ) ;
block . builders . destroy . addLine ( ` ${ if_name } ${ name } .d(); ` ) ;
}
block . builders . destroy . addLine ( ` ${ if_name } ${ name } .d(); ` ) ;
}
// if any of the siblings have outros, we need to keep references to the blocks
// (TODO does this only apply to bidi transitions?)
function compoundWithOutros (
generator : DomGenerator ,
block : Block ,
parentNode : string ,
parentNodes : string ,
node : Node ,
branches ,
dynamic ,
{ name , anchor , hasElse }
) {
const select_block_type = block . getUniqueName ( ` select_block_type ` ) ;
const current_block_type_index = block . getUniqueName ( ` current_block_type_index ` ) ;
const previous_block_index = block . getUniqueName ( ` previous_block_index ` ) ;
const if_block_creators = block . getUniqueName ( ` if_block_creators ` ) ;
const if_blocks = block . getUniqueName ( ` if_blocks ` ) ;
const if_current_block_type_index = hasElse
? ''
: ` if (~ ${ current_block_type_index } ) ` ;
block . addVariable ( current_block_type_index ) ;
block . addVariable ( name ) ;
block . builders . init . addBlock ( deindent `
var $ { if_block_creators } = [
$ { branches . map ( branch = > branch . block ) . join ( ',\n' ) }
getBranches (
block : Block ,
parentNode : string ,
parentNodes : string ,
node : Node
) {
block . contextualise ( node . expression ) ; // TODO remove
const branches = [
{
condition : node.metadata.snippet ,
block : node.block.name ,
hasUpdateMethod : node.block.hasUpdateMethod ,
hasIntroMethod : node.block.hasIntroMethod ,
hasOutroMethod : node.block.hasOutroMethod ,
} ,
] ;
var $ { if_blocks } = [ ] ;
this . visitChildren ( block , node ) ;
function $ { select_block_type } ( state ) {
$ { branches
. map ( ( { condition , block } , i ) = > ` ${ condition ? ` if ( ${ condition } ) ` : '' } return ${ block ? i : - 1 } ; ` )
. join ( '\n' ) }
}
` );
if ( isElseIf ( node . else ) ) {
branches . push (
. . . this . getBranches ( block , parentNode , parentNodes , node . else . children [ 0 ] )
) ;
} else {
branches . push ( {
condition : null ,
block : node.else ? node.else.block.name : null ,
hasUpdateMethod : node.else ? node.else.block.hasUpdateMethod : false ,
hasIntroMethod : node.else ? node.else.block.hasIntroMethod : false ,
hasOutroMethod : node.else ? node.else.block.hasOutroMethod : false ,
} ) ;
if ( hasElse ) {
block . builders . init . addBlock ( deindent `
$ { current_block_type_index } = $ { select_block_type } ( state ) ;
$ { name } = $ { if_blocks } [ $ { current_block_type_index } ] = $ { if_block_creators } [ $ { current_block_type_index } ] ( # component , state ) ;
` );
} else {
block . builders . init . addBlock ( deindent `
if ( ~ ( $ { current_block_type_index } = $ { select_block_type } ( state ) ) ) {
$ { name } = $ { if_blocks } [ $ { current_block_type_index } ] = $ { if_block_creators } [ $ { current_block_type_index } ] ( # component , state ) ;
if ( node . else ) {
this . visitChildren ( block , node . else ) ;
}
` );
}
const mountOrIntro = branches [ 0 ] . hasIntroMethod ? 'i' : 'm' ;
const initialMountNode = parentNode || '#target' ;
const anchorNode = parentNode ? 'null' : 'anchor' ;
block . builders . mount . addLine (
` ${ if_current_block_type_index } ${ if_blocks } [ ${ current_block_type_index } ]. ${ mountOrIntro } ( ${ initialMountNode } , ${ anchorNode } ); `
) ;
const updateMountNode = node . getUpdateMountNode ( anchor ) ;
const destroyOldBlock = deindent `
$ { name } . o ( function ( ) {
$ { if_blocks } [ $ { previous_block_index } ] . u ( ) ;
$ { if_blocks } [ $ { previous_block_index } ] . d ( ) ;
$ { if_blocks } [ $ { previous_block_index } ] = null ;
} ) ;
` ;
const createNewBlock = deindent `
$ { name } = $ { if_blocks } [ $ { current_block_type_index } ] ;
if ( ! $ { name } ) {
$ { name } = $ { if_blocks } [ $ { current_block_type_index } ] = $ { if_block_creators } [ $ { current_block_type_index } ] ( # component , state ) ;
$ { name } . c ( ) ;
}
$ { name } . $ { mountOrIntro } ( $ { updateMountNode } , $ { anchor } ) ;
` ;
const changeBlock = hasElse
? deindent `
$ { destroyOldBlock }
$ { createNewBlock }
`
: deindent `
if ( $ { name } ) {
$ { destroyOldBlock }
}
if ( ~ $ { current_block_type_index } ) {
$ { createNewBlock }
} else {
$ { name } = null ;
}
` ;
if ( dynamic ) {
block . builders . update . addBlock ( deindent `
var $ { previous_block_index } = $ { current_block_type_index } ;
$ { current_block_type_index } = $ { select_block_type } ( state ) ;
if ( $ { current_block_type_index } === $ { previous_block_index } ) {
$ { if_current_block_type_index } $ { if_blocks } [ $ { current_block_type_index } ] . p ( changed , state ) ;
} else {
$ { changeBlock }
}
` );
} else {
block . builders . update . addBlock ( deindent `
var $ { previous_block_index } = $ { current_block_type_index } ;
$ { current_block_type_index } = $ { select_block_type } ( state ) ;
if ( $ { current_block_type_index } !== $ { previous_block_index } ) {
$ { changeBlock }
}
` );
return branches ;
}
block . builders . destroy . addLine ( deindent `
$ { if_current_block_type_index } {
$ { if_blocks } [ $ { current_block_type_index } ] . u ( ) ;
$ { if_blocks } [ $ { current_block_type_index } ] . d ( ) ;
}
` );
visitChildren ( block : Block , node : Node ) {
node . children . forEach ( ( child : Node ) = > {
child . build ( node . block , null , 'nodes' ) ;
} ) ;
}
}