@ -101,7 +101,7 @@ export default class IfBlock extends Node {
? block . getUniqueName ( ` ${ name } _anchor ` )
? block . getUniqueName ( ` ${ name } _anchor ` )
: ( this . next && this . next . var ) || 'null' ;
: ( 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 hasElse = isElseBranch ( branches [ branches . length - 1 ] ) ;
const if_name = hasElse ? '' : ` if ( ${ name } ) ` ;
const if_name = hasElse ? '' : ` if ( ${ name } ) ` ;
@ -113,21 +113,12 @@ export default class IfBlock extends Node {
if ( this . else ) {
if ( this . else ) {
if ( hasOutros ) {
if ( hasOutros ) {
compoundWithOutros (
this . buildCompoundWithOutros ( block , parentNode , parentNodes , branches , dynamic , vars ) ;
this . generator ,
block ,
parentNode ,
parentNodes ,
this ,
branches ,
dynamic ,
vars
) ;
} else {
} else {
compound ( this . generator , block , parentNode , parentNode s, thi s, branches , dynamic , vars ) ;
this . buildCompound ( block , parentNode , parentNodes , branches , dynamic , vars ) ;
}
}
} else {
} 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(); ` ) ;
block . builders . create . addLine ( ` ${ if_name } ${ name } .c(); ` ) ;
@ -147,173 +138,20 @@ export default class IfBlock extends Node {
) ;
) ;
}
}
}
}
}
// TODO move all this into the class
function getBranches (
buildCompound (
generator : DomGenerator ,
block : Block ,
block : Block ,
parentNode : string ,
parentNode : string ,
parentNodes : 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 ,
} ,
] ;
visitChildren ( generator , block , node ) ;
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 ,
} ) ;
if ( node . else ) {
visitChildren ( generator , block , node . else ) ;
}
}
return branches ;
}
function visitChildren (
generator : DomGenerator ,
block : Block ,
node : Node
) {
node . children . forEach ( ( child : Node ) = > {
child . build ( node . block , null , 'nodes' ) ;
} ) ;
}
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 ) ;
` );
const mountOrIntro = branch . hasIntroMethod ? 'i' : 'm' ;
const initialMountNode = parentNode || '#target' ;
const anchorNode = parentNode ? 'null' : 'anchor' ;
block . builders . mount . addLine (
` if ( ${ name } ) ${ name } . ${ mountOrIntro } ( ${ initialMountNode } , ${ anchorNode } ); `
) ;
const updateMountNode = node . 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 } . 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 }
}
` );
block . builders . unmount . addLine ( ` ${ if_name } ${ name } .u(); ` ) ;
block . builders . destroy . addLine ( ` ${ if_name } ${ name } .d(); ` ) ;
}
function compound (
generator : DomGenerator ,
block : Block ,
parentNode : string ,
parentNodes : string ,
node : Node ,
branches ,
branches ,
dynamic ,
dynamic ,
{ name , anchor , hasElse , if_name }
{ name , anchor , hasElse , if_name }
) {
) {
const select_block_type = generator . getUniqueName ( ` select_block_type ` ) ;
const select_block_type = this . generator . getUniqueName ( ` select_block_type ` ) ;
const current_block_type = block . getUniqueName ( ` current_block_type ` ) ;
const current_block_type = block . getUniqueName ( ` current_block_type ` ) ;
const current_block_type_and = hasElse ? '' : ` ${ current_block_type } && ` ;
const current_block_type_and = hasElse ? '' : ` ${ current_block_type } && ` ;
generator . blocks . push ( deindent `
block . builders . init . addBlock ( deindent `
function $ { select_block_type } ( state ) {
function $ { select_block_type } ( state ) {
$ { branches
$ { branches
. map ( ( { condition , block } ) = > ` ${ condition ? ` if ( ${ condition } ) ` : '' } return ${ block } ; ` )
. map ( ( { condition , block } ) = > ` ${ condition ? ` if ( ${ condition } ) ` : '' } return ${ block } ; ` )
@ -334,7 +172,7 @@ function compound(
` ${ if_name } ${ name } . ${ mountOrIntro } ( ${ initialMountNode } , ${ anchorNode } ); `
` ${ if_name } ${ name } . ${ mountOrIntro } ( ${ initialMountNode } , ${ anchorNode } ); `
) ;
) ;
const updateMountNode = node . getUpdateMountNode ( anchor ) ;
const updateMountNode = this . getUpdateMountNode ( anchor ) ;
const changeBlock = deindent `
const changeBlock = deindent `
$ { hasElse
$ { hasElse
@ -371,20 +209,18 @@ function compound(
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
// if any of the siblings have outros, we need to keep references to the blocks
// (TODO does this only apply to bidi transitions?)
// (TODO does this only apply to bidi transitions?)
function compoundWithOutros (
buildCompoundWithOutros (
generator : DomGenerator ,
block : Block ,
block : Block ,
parentNode : string ,
parentNode : string ,
parentNodes : string ,
parentNodes : string ,
node : Node ,
branches ,
branches ,
dynamic ,
dynamic ,
{ name , anchor , hasElse }
{ name , anchor , hasElse }
) {
) {
const select_block_type = block . getUniqueName ( ` select_block_type ` ) ;
const select_block_type = block . getUniqueName ( ` select_block_type ` ) ;
const current_block_type_index = block . getUniqueName ( ` current_block_type_index ` ) ;
const current_block_type_index = block . getUniqueName ( ` current_block_type_index ` ) ;
const previous_block_index = block . getUniqueName ( ` previous_block_index ` ) ;
const previous_block_index = block . getUniqueName ( ` previous_block_index ` ) ;
@ -433,7 +269,7 @@ function compoundWithOutros(
` ${ if_current_block_type_index } ${ if_blocks } [ ${ current_block_type_index } ]. ${ mountOrIntro } ( ${ initialMountNode } , ${ anchorNode } ); `
` ${ if_current_block_type_index } ${ if_blocks } [ ${ current_block_type_index } ]. ${ mountOrIntro } ( ${ initialMountNode } , ${ anchorNode } ); `
) ;
) ;
const updateMountNode = node . getUpdateMountNode ( anchor ) ;
const updateMountNode = this . getUpdateMountNode ( anchor ) ;
const destroyOldBlock = deindent `
const destroyOldBlock = deindent `
$ { name } . o ( function ( ) {
$ { name } . o ( function ( ) {
@ -496,4 +332,140 @@ function compoundWithOutros(
$ { if_blocks } [ $ { current_block_type_index } ] . d ( ) ;
$ { if_blocks } [ $ { current_block_type_index } ] . d ( ) ;
}
}
` );
` );
}
buildSimple (
block : Block ,
parentNode : string ,
parentNodes : string ,
branch ,
dynamic ,
{ name , anchor , if_name }
) {
block . builders . init . addBlock ( deindent `
var $ { name } = ( $ { branch . condition } ) && $ { branch . block } ( # component , state ) ;
` );
const mountOrIntro = branch . hasIntroMethod ? 'i' : 'm' ;
const initialMountNode = parentNode || '#target' ;
const anchorNode = parentNode ? 'null' : 'anchor' ;
block . builders . mount . addLine (
` if ( ${ name } ) ${ name } . ${ mountOrIntro } ( ${ initialMountNode } , ${ anchorNode } ); `
) ;
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 } . 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 }
}
` );
block . builders . unmount . addLine ( ` ${ if_name } ${ name } .u(); ` ) ;
block . builders . destroy . addLine ( ` ${ if_name } ${ name } .d(); ` ) ;
}
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 ,
} ,
] ;
this . visitChildren ( block , node ) ;
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 ( node . else ) {
this . visitChildren ( block , node . else ) ;
}
}
return branches ;
}
visitChildren ( block : Block , node : Node ) {
node . children . forEach ( ( child : Node ) = > {
child . build ( node . block , null , 'nodes' ) ;
} ) ;
}
}
}