Component class directives

pull/4749/head
Timothy Johnson 6 years ago
parent 628b09d867
commit e48343af53

@ -6,6 +6,7 @@ import EventHandler from './EventHandler';
import Expression from './shared/Expression'; import Expression from './shared/Expression';
import Component from '../Component'; import Component from '../Component';
import Let from './Let'; import Let from './Let';
import Class from './Class';
import TemplateScope from './shared/TemplateScope'; import TemplateScope from './shared/TemplateScope';
import { INode } from './interfaces'; import { INode } from './interfaces';
@ -15,6 +16,7 @@ export default class InlineComponent extends Node {
expression: Expression; expression: Expression;
attributes: Attribute[] = []; attributes: Attribute[] = [];
bindings: Binding[] = []; bindings: Binding[] = [];
classes: Class[] = [];
handlers: EventHandler[] = []; handlers: EventHandler[] = [];
lets: Let[] = []; lets: Let[] = [];
children: INode[]; children: INode[];
@ -61,10 +63,8 @@ export default class InlineComponent extends Node {
break; break;
case 'Class': case 'Class':
component.error(node, { this.classes.push(new Class(component, this, scope, node));
code: `invalid-class`, break;
message: `Classes can only be applied to DOM elements, not components`
});
case 'EventHandler': case 'EventHandler':
this.handlers.push(new EventHandler(component, this, scope, node)); this.handlers.push(new EventHandler(component, this, scope, node));

@ -152,6 +152,13 @@ export default class InlineComponentWrapper extends Wrapper {
const uses_spread = !!this.node.attributes.find(a => a.is_spread); const uses_spread = !!this.node.attributes.find(a => a.is_spread);
const uses_classes = this.node.classes.length > 0
const classNames: Identifier = {
type: 'Identifier',
name: '#classNames'
};
// removing empty slot // removing empty slot
for (const slot of this.slots.keys()) { for (const slot of this.slots.keys()) {
if (!this.slots.get(slot).block.has_content()) { if (!this.slots.get(slot).block.has_content()) {
@ -173,14 +180,19 @@ export default class InlineComponentWrapper extends Wrapper {
] ]
: []; : [];
const attributes = uses_classes
? this.node.attributes.filter(a => a.name !== 'class')
: this.node.attributes
const attribute_object = uses_spread const attribute_object = uses_spread
? x`{ ${initial_props} }` ? x`{ ${initial_props} }`
: x`{ : x`{
${this.node.attributes.map(attr => p`${attr.name}: ${attr.get_value(block)}`)}, ${attributes.map(attr => p`${attr.name}: ${attr.get_value(block)}`)},
${uses_classes ? p`class: @component_classnames(${classNames})` : null},
${initial_props} ${initial_props}
}`; }`;
if (this.node.attributes.length || this.node.bindings.length || initial_props.length) { if (this.node.attributes.length || this.node.bindings.length || initial_props.length || uses_classes) {
if (!uses_spread && this.node.bindings.length === 0) { if (!uses_spread && this.node.bindings.length === 0) {
component_opts.properties.push(p`props: ${attribute_object}`); component_opts.properties.push(p`props: ${attribute_object}`);
} else { } else {
@ -207,9 +219,9 @@ export default class InlineComponentWrapper extends Wrapper {
}); });
}); });
const dynamic_attributes = this.node.attributes.filter(a => a.get_dependencies().length > 0); const dynamic_attributes = attributes.filter(a => a.get_dependencies().length > 0);
if (!uses_spread && (dynamic_attributes.length > 0 || this.node.bindings.length > 0 || fragment_dependencies.size > 0)) { if (!uses_spread && (dynamic_attributes.length > 0 || this.node.bindings.length > 0 || fragment_dependencies.size > 0 || uses_classes)) {
updates.push(b`const ${name_changes} = {};`); updates.push(b`const ${name_changes} = {};`);
} }
@ -298,6 +310,73 @@ export default class InlineComponentWrapper extends Wrapper {
} }
} }
if (uses_classes) {
const initial_class_values = [];
const all_dependencies = [];
this.node.classes.forEach(class_directive => {
const { expression, name } = class_directive;
let snippet;
let dependencies;
if (expression) {
snippet = expression.manipulate(block);
dependencies = expression.dependencies;
} else {
snippet = name;
dependencies = new Set([name]);
}
initial_class_values.push(p`${name}: ${snippet}`);
if ((dependencies && dependencies.size > 0)) {
all_dependencies.push(...dependencies);
const condition = block.renderer.dirty(Array.from(dependencies));
updates.push(b`
if (${condition}) {
${classNames}.${name} = ${snippet};
}`);
}
});
const attribute = this.node.attributes.find((a) => a.name === 'class');
if (attribute) {
const dependencies = attribute.get_dependencies();
const condition = block.renderer.dirty(Array.from(dependencies));
const value = attribute.get_value(block)
updates.push(b`if (${condition}) {
${classNames}.$$class = ${value};
}`)
all_dependencies.push(...dependencies);
initial_class_values.push(p`$$class: ${value}`);
}
if (uses_spread) {
statements.push(b`
if (${props}.class) {
${classNames}.$$class = ${props}.class;
}
${props}.class = @component_classnames(${classNames});
`);
updates.push(b`
if (${name_changes}.class) {
${classNames}.$$class = ${name_changes}.class
}
${name_changes}.class = @component_classnames(${classNames});
`);
} else if (all_dependencies.length > 0) {
const condition = block.renderer.dirty(all_dependencies);
updates.push(b`
if (${condition}) {
${name_changes}.class = @component_classnames(${classNames});
}`);
}
block.add_variable(classNames, x`{${initial_class_values}}`);
}
if (fragment_dependencies.size > 0) { if (fragment_dependencies.size > 0) {
updates.push(b` updates.push(b`
if (${renderer.dirty(Array.from(fragment_dependencies))}) { if (${renderer.dirty(Array.from(fragment_dependencies))}) {

@ -35,21 +35,39 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend
let props; let props;
let attributes = node.attributes
let classProp
if (node.classes.length > 0) {
const index = attributes.findIndex(a => a.name === 'class')
const attr = attributes[index]
attributes.splice(index, 1)
classProp = p`
class: @component_classnames({
${node.classes.map(class_directive => {
const { expression, name } = class_directive;
const snippet = expression ? expression.node : x`#ctx.${name}`; // TODO is this right?
return p`${name}: ${snippet}`;
})},
${attr ? p`$$class: ${get_prop_value(attr)}` : null}
})
`
}
if (uses_spread) { if (uses_spread) {
props = x`@_Object.assign(${ props = x`@_Object.assign(${
node.attributes attributes
.map(attribute => { .map(attribute => attribute.is_spread
if (attribute.is_spread) { ? attribute.expression.node
return attribute.expression.node; : x`{ ${attribute.name}: ${get_prop_value(attribute)} }`
} else { )
return x`{ ${attribute.name}: ${get_prop_value(attribute)} }`; },
} ${classProp ? x`{ ${classProp} }` : null},
}) ${binding_props.map(p => x`{ ${p} }`)}
.concat(binding_props.map(p => x`{ ${p} }`)) )`;
})`;
} else { } else {
props = x`{ props = x`{
${node.attributes.map(attribute => p`${attribute.name}: ${get_prop_value(attribute)}`)}, ${attributes.map(attribute => p`${attribute.name}: ${get_prop_value(attribute)}`)},
${classProp},
${binding_props} ${binding_props}
}`; }`;
} }

@ -44,6 +44,16 @@ export function bind(component, name, callback) {
} }
} }
export function component_classnames (classNames) {
let str = classNames.$$class || '';
for (const name in classNames) {
if (name == '$$class') continue;
if (classNames[name]) str += ' ' + name;
}
return str.trim();
}
export function create_component(block) { export function create_component(block) {
block && block.c(); block && block.c();
} }

Loading…
Cancel
Save