|
|
import deindent from '../../../../utils/deindent.js';
|
|
|
import visit from '../../visit.js';
|
|
|
import visitComponent from '../Component/Component.js';
|
|
|
import visitWindow from './meta/Window.js';
|
|
|
import visitAttribute from './Attribute.js';
|
|
|
import visitEventHandler from './EventHandler.js';
|
|
|
import visitBinding from './Binding.js';
|
|
|
import visitRef from './Ref.js';
|
|
|
|
|
|
const meta = {
|
|
|
':Window': visitWindow
|
|
|
};
|
|
|
|
|
|
const order = {
|
|
|
Attribute: 1,
|
|
|
Binding: 2,
|
|
|
EventHandler: 3,
|
|
|
Ref: 4
|
|
|
};
|
|
|
|
|
|
const visitors = {
|
|
|
Attribute: visitAttribute,
|
|
|
EventHandler: visitEventHandler,
|
|
|
Binding: visitBinding,
|
|
|
Ref: visitRef
|
|
|
};
|
|
|
|
|
|
export default function visitElement ( generator, block, state, node ) {
|
|
|
if ( node.name in meta ) {
|
|
|
return meta[ node.name ]( generator, block, node );
|
|
|
}
|
|
|
|
|
|
if ( generator.components.has( node.name ) || node.name === ':Self' ) {
|
|
|
return visitComponent( generator, block, state, node );
|
|
|
}
|
|
|
|
|
|
const name = block.getUniqueName( node.name.replace( /[^a-zA-Z_$]/g, '_' ) );
|
|
|
|
|
|
const childState = Object.assign( {}, state, {
|
|
|
isTopLevel: false,
|
|
|
parentNode: name,
|
|
|
parentNodeName: node.name,
|
|
|
namespace: node.name === 'svg' ? 'http://www.w3.org/2000/svg' : state.namespace,
|
|
|
allUsedContexts: []
|
|
|
});
|
|
|
|
|
|
block.builders.create.addLine( `var ${name} = ${getRenderStatement( generator, childState.namespace, node.name )};` );
|
|
|
block.mount( name, state.parentNode );
|
|
|
|
|
|
if ( !state.parentNode ) {
|
|
|
block.builders.detach.addLine( `${generator.helper( 'detachNode' )}( ${name} );` );
|
|
|
}
|
|
|
|
|
|
// add CSS encapsulation attribute
|
|
|
if ( generator.cssId && state.isTopLevel ) {
|
|
|
block.builders.create.addLine( `${generator.helper( 'setAttribute' )}( ${name}, '${generator.cssId}', '' );` );
|
|
|
}
|
|
|
|
|
|
let selectValueAttribute;
|
|
|
|
|
|
node.attributes
|
|
|
.sort( ( a, b ) => order[ a.type ] - order[ b.type ] )
|
|
|
.forEach( attribute => {
|
|
|
// <select> value attributes are an annoying special case — it must be handled
|
|
|
// *after* its children have been updated
|
|
|
if ( ( attribute.type === 'Attribute' || attribute.type === 'Binding' ) && attribute.name === 'value' && node.name === 'select' ) {
|
|
|
selectValueAttribute = attribute;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
visitors[ attribute.type ]( generator, block, childState, node, attribute );
|
|
|
});
|
|
|
|
|
|
// special case – bound <option> without a value attribute
|
|
|
if ( node.name === 'option' && !node.attributes.find( attribute => attribute.type === 'Attribute' && attribute.name === 'value' ) ) { // TODO check it's bound
|
|
|
const statement = `${name}.__value = ${name}.textContent;`;
|
|
|
node.initialUpdate = node.lateUpdate = statement;
|
|
|
}
|
|
|
|
|
|
if ( childState.allUsedContexts.length || childState.usesComponent ) {
|
|
|
const initialProps = [];
|
|
|
const updates = [];
|
|
|
|
|
|
if ( childState.usesComponent ) {
|
|
|
initialProps.push( `component: ${block.component}` );
|
|
|
}
|
|
|
|
|
|
childState.allUsedContexts.forEach( contextName => {
|
|
|
if ( contextName === 'state' ) return;
|
|
|
|
|
|
const listName = block.listNames.get( contextName );
|
|
|
const indexName = block.indexNames.get( contextName );
|
|
|
|
|
|
initialProps.push( `${listName}: ${listName},\n${indexName}: ${indexName}` );
|
|
|
updates.push( `${name}._svelte.${listName} = ${listName};\n${name}._svelte.${indexName} = ${indexName};` );
|
|
|
});
|
|
|
|
|
|
if ( initialProps.length ) {
|
|
|
block.builders.create.addBlock( deindent`
|
|
|
${name}._svelte = {
|
|
|
${initialProps.join( ',\n' )}
|
|
|
};
|
|
|
` );
|
|
|
}
|
|
|
|
|
|
if ( updates.length ) {
|
|
|
block.builders.update.addBlock( updates.join( '\n' ) );
|
|
|
}
|
|
|
}
|
|
|
|
|
|
node.children.forEach( child => {
|
|
|
visit( generator, block, childState, child );
|
|
|
});
|
|
|
|
|
|
if ( node.lateUpdate ) {
|
|
|
block.builders.update.addLine( node.lateUpdate );
|
|
|
}
|
|
|
|
|
|
if ( selectValueAttribute ) {
|
|
|
const visitor = selectValueAttribute.type === 'Attribute' ? visitAttribute : visitBinding;
|
|
|
visitor( generator, block, childState, node, selectValueAttribute );
|
|
|
}
|
|
|
|
|
|
if ( node.initialUpdate ) {
|
|
|
block.builders.create.addBlock( node.initialUpdate );
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function getRenderStatement ( generator, namespace, name ) {
|
|
|
if ( namespace === 'http://www.w3.org/2000/svg' ) {
|
|
|
return `${generator.helper( 'createSvgElement' )}( '${name}' )`;
|
|
|
}
|
|
|
|
|
|
if ( namespace ) {
|
|
|
return `document.createElementNS( '${namespace}', '${name}' )`;
|
|
|
}
|
|
|
|
|
|
return `${generator.helper( 'createElement' )}( '${name}' )`;
|
|
|
}
|