fix scoping class and `class:` with spread attributes (#3790)

pull/3792/head
Conduitry 6 years ago
parent 33c8cd3329
commit 73b5bd2dd6

@ -105,6 +105,7 @@ export default class Element extends Node {
animation?: Animation = null; animation?: Animation = null;
children: INode[]; children: INode[];
namespace: string; namespace: string;
needs_manual_style_scoping: boolean;
constructor(component, parent, scope, info: any) { constructor(component, parent, scope, info: any) {
super(component, parent, scope, info); super(component, parent, scope, info);
@ -712,6 +713,11 @@ export default class Element extends Node {
} }
add_css_class() { add_css_class() {
if (this.attributes.some(attr => attr.is_spread)) {
this.needs_manual_style_scoping = true;
return;
}
const { id } = this.component.stylesheet; const { id } = this.component.stylesheet;
const class_attribute = this.attributes.find(a => a.name === 'class'); const class_attribute = this.attributes.find(a => a.name === 'class');

@ -344,6 +344,7 @@ export default class ElementWrapper extends Wrapper {
this.add_animation(block); this.add_animation(block);
this.add_actions(block); this.add_actions(block);
this.add_classes(block); this.add_classes(block);
this.add_manual_style_scoping(block);
if (nodes && this.renderer.options.hydratable) { if (nodes && this.renderer.options.hydratable) {
block.chunks.claim.push( block.chunks.claim.push(
@ -838,6 +839,14 @@ export default class ElementWrapper extends Wrapper {
} }
}); });
} }
add_manual_style_scoping(block) {
if (this.node.needs_manual_style_scoping) {
const updater = b`@toggle_class(${this.var}, "${this.node.component.stylesheet.id}", true);`;
block.chunks.hydrate.push(updater);
block.chunks.update.push(updater);
}
}
} }
function to_html(wrappers: Array<ElementWrapper | TextWrapper>, block: Block, literal: any, state: any) { function to_html(wrappers: Array<ElementWrapper | TextWrapper>, block: Block, literal: any, state: any) {

@ -1,5 +1,4 @@
import { is_void } from '../../../utils/names'; import { is_void } from '../../../utils/names';
import Class from '../../nodes/Class';
import { get_attribute_value, get_class_attribute_value } from './shared/get_attribute_value'; import { get_attribute_value, get_class_attribute_value } from './shared/get_attribute_value';
import { get_slot_scope } from './shared/get_slot_scope'; import { get_slot_scope } from './shared/get_slot_scope';
import Renderer, { RenderOptions } from '../Renderer'; import Renderer, { RenderOptions } from '../Renderer';
@ -69,15 +68,17 @@ export default function(node: Element, renderer: Renderer, options: RenderOption
renderer.add_string(`<${node.name}`); renderer.add_string(`<${node.name}`);
const class_expression = node.classes.length > 0 && node.classes const class_expression_list = node.classes.map(class_directive => {
.map((class_directive: Class) => { const { expression, name } = class_directive;
const { expression, name } = class_directive; const snippet = expression ? expression.node : x`#ctx.${name}`;
const snippet = expression ? expression.node : x`#ctx.${name}`; return x`${snippet} ? "${name}" : ""`;
return x`${snippet} ? "${name}" : ""`; });
}) if (node.needs_manual_style_scoping) {
.reduce((lhs, rhs) => x`${lhs} + ' ' + ${rhs}`); class_expression_list.push(x`"${node.component.stylesheet.id}"`);
}
let add_class_attribute = class_expression ? true : false; const class_expression =
class_expression_list.length > 0 &&
class_expression_list.reduce((lhs, rhs) => x`${lhs} + ' ' + ${rhs}`);
if (node.attributes.some(attr => attr.is_spread)) { if (node.attributes.some(attr => attr.is_spread)) {
// TODO dry this out // TODO dry this out
@ -98,17 +99,15 @@ export default function(node: Element, renderer: Renderer, options: RenderOption
) { ) {
// a boolean attribute with one non-Text chunk // a boolean attribute with one non-Text chunk
args.push(x`{ ${attribute.name}: ${(attribute.chunks[0] as Expression).node} || null }`); args.push(x`{ ${attribute.name}: ${(attribute.chunks[0] as Expression).node} || null }`);
} else if (name === 'class' && class_expression) {
// Add class expression
args.push(x`{ ${attribute.name}: [${get_class_attribute_value(attribute)}, ${class_expression}].join(' ').trim() }`);
} else { } else {
args.push(x`{ ${attribute.name}: ${(name === 'class' ? get_class_attribute_value : get_attribute_value)(attribute)} }`); args.push(x`{ ${attribute.name}: ${get_attribute_value(attribute)} }`);
} }
} }
}); });
renderer.add_expression(x`@spread([${args}])`); renderer.add_expression(x`@spread([${args}], ${class_expression});`);
} else { } else {
let add_class_attribute = !!class_expression;
node.attributes.forEach(attribute => { node.attributes.forEach(attribute => {
const name = attribute.name.toLowerCase(); const name = attribute.name.toLowerCase();
if (name === 'value' && node.name.toLowerCase() === 'textarea') { if (name === 'value' && node.name.toLowerCase() === 'textarea') {
@ -137,6 +136,9 @@ export default function(node: Element, renderer: Renderer, options: RenderOption
renderer.add_string(`"`); renderer.add_string(`"`);
} }
}); });
if (add_class_attribute) {
renderer.add_expression(x`@add_classes([${class_expression}].join(' ').trim())`);
}
} }
node.bindings.forEach(binding => { node.bindings.forEach(binding => {
@ -162,10 +164,6 @@ export default function(node: Element, renderer: Renderer, options: RenderOption
} }
}); });
if (add_class_attribute) {
renderer.add_expression(x`@add_classes([${class_expression}].join(' ').trim())`);
}
renderer.add_string('>'); renderer.add_string('>');
if (node_contents !== undefined) { if (node_contents !== undefined) {

@ -5,8 +5,15 @@ export const invalid_attribute_name_character = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFF
// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
// https://infra.spec.whatwg.org/#noncharacter // https://infra.spec.whatwg.org/#noncharacter
export function spread(args) { export function spread(args, classes_to_add) {
const attributes = Object.assign({}, ...args); const attributes = Object.assign({}, ...args);
if (classes_to_add) {
if (attributes.class == null) {
attributes.class = classes_to_add;
} else {
attributes.class += ' ' + classes_to_add;
}
}
let str = ''; let str = '';
Object.keys(attributes).forEach(name => { Object.keys(attributes).forEach(name => {

Loading…
Cancel
Save