wip: adding multi class by separator feature

pull/4349/head
Marcelo Jr 6 years ago
parent 83d461f537
commit 89cfaeb859

@ -1348,29 +1348,32 @@ function process_component_options(component: Component, nodes) {
const { name } = attribute;
switch (name) {
case 'classSeparator':
case 'tag': {
const code = 'invalid-tag-attribute';
const message = `'tag' must be a string literal`;
const tag = get_value(attribute, code, message);
const message = `'${name}' must be a string literal`;
const value = get_value(attribute, code, message);
if (typeof tag !== 'string' && tag !== null)
if (typeof value !== 'string' && value !== null)
component.error(attribute, { code, message });
if (tag && !/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/.test(tag)) {
component.error(attribute, {
code: `invalid-tag-property`,
message: `tag name must be two or more words joined by the '-' character`,
});
}
if (value === 'tag') {
if (value && !/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/.test(value)) {
component.error(attribute, {
code: `invalid-tag-property`,
message: `tag name must be two or more words joined by the '-' character`,
});
}
if (tag && !component.compile_options.customElement) {
component.warn(attribute, {
code: 'missing-custom-element-compile-options',
message: `The 'tag' option is used when generating a custom element. Did you forget the 'customElement: true' compile option?`
});
if (value && !component.compile_options.customElement) {
component.warn(attribute, {
code: 'missing-custom-element-compile-options',
message: `The 'tag' option is used when generating a custom element. Did you forget the 'customElement: true' compile option?`
});
}
}
component_options.tag = tag;
component_options[name] = value;
break;
}

@ -26,7 +26,8 @@ const valid_options = [
'css',
'loopGuardTimeout',
'preserveComments',
'preserveWhitespace'
'preserveWhitespace',
'classSeparator'
];
function validate_options(options: CompileOptions, warnings: Warning[]) {

@ -26,6 +26,7 @@ import { Identifier } from 'estree';
import EventHandler from './EventHandler';
import { extract_names } from 'periscopic';
import Action from '../../../nodes/Action';
import Expression from '../../../nodes/shared/Expression';
const events = [
{
@ -876,31 +877,38 @@ export default class ElementWrapper extends Wrapper {
const has_spread = this.node.attributes.some(attr => attr.is_spread);
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]);
}
const updater = b`@toggle_class(${this.var}, "${name}", ${snippet});`;
block.chunks.hydrate.push(updater);
name.split(this.renderer.options.classSeparator).forEach((split_name: string) => {
this.add_class(block, expression, split_name, has_spread);
});
});
}
add_class(block: Block, expression: Expression, name: string, has_spread: boolean) {
let snippet;
let dependencies;
if (expression) {
snippet = expression.manipulate(block);
dependencies = expression.dependencies;
} else {
snippet = name;
dependencies = new Set([name]);
}
const updater = b`@toggle_class(${this.var}, "${name}", ${snippet});`;
if (has_spread) {
block.chunks.update.push(updater);
} else if ((dependencies && dependencies.size > 0) || this.class_dependencies.length) {
const all_dependencies = this.class_dependencies.concat(...dependencies);
const condition = block.renderer.dirty(all_dependencies);
block.chunks.hydrate.push(updater);
block.chunks.update.push(b`
if (${condition}) {
${updater}
}`);
}
});
if (has_spread) {
block.chunks.update.push(updater);
} else if ((dependencies && dependencies.size > 0) || this.class_dependencies.length) {
const all_dependencies = this.class_dependencies.concat(...dependencies);
const condition = block.renderer.dirty(all_dependencies);
block.chunks.update.push(b`
if (${condition}) {
${updater}
}`);
}
}
add_manual_style_scoping(block) {

@ -30,8 +30,22 @@ export default function(node: Element, renderer: Renderer, options: RenderOption
const class_expression_list = node.classes.map(class_directive => {
const { expression, name } = class_directive;
const snippet = expression ? expression.node : x`#ctx.${name}`; // TODO is this right?
return x`${snippet} ? "${name}" : ""`;
const name_has_separator = name.includes(options.classSeparator);
let parsed_name = name;
if (name_has_separator) {
parsed_name = `"${name.split(",").join(' ')}"`;
}
if (name_has_separator && expression) {
// TODO: we have a wrong scenario here, "class:one,two". We should treat this case, showing error?
return parsed_name;
}
const snippet = expression ? expression.node : x`#ctx.${name}`;
return x`${snippet} ? "${parsed_name}" : ""`;
});
if (node.needs_manual_style_scoping) {
class_expression_list.push(x`"${node.component.stylesheet.id}"`);

@ -126,6 +126,8 @@ export interface CompileOptions {
preserveComments?: boolean;
preserveWhitespace?: boolean;
classSeparator?: string;
}
export interface ParserOptions {

@ -79,6 +79,7 @@ describe("runtime", () => {
compileOptions.hydratable = hydrate;
compileOptions.immutable = config.immutable;
compileOptions.accessors = 'accessors' in config ? config.accessors : true;
compileOptions.classSeparator = 'classSeparator' in config ? config.classSeparator : undefined;
cleanRequireCache();

@ -0,0 +1,6 @@
export default {
classSeparator: ',',
skip_if_ssr: true,
html: `<div class="one two"></div>`,
};
Loading…
Cancel
Save