allow to use class directive on <svelte:body> (#3105)

pull/6928/head
Andi Rady Kurniawan 5 years ago
parent d5370f23d3
commit 9a5fd562d5

@ -1,6 +1,7 @@
import Node from './shared/Node'; import Node from './shared/Node';
import EventHandler from './EventHandler'; import EventHandler from './EventHandler';
import Action from './Action'; import Action from './Action';
import Class from './Class';
import Component from '../Component'; import Component from '../Component';
import TemplateScope from './shared/TemplateScope'; import TemplateScope from './shared/TemplateScope';
import { Element } from '../../interfaces'; import { Element } from '../../interfaces';
@ -9,6 +10,7 @@ export default class Body extends Node {
type: 'Body'; type: 'Body';
handlers: EventHandler[] = []; handlers: EventHandler[] = [];
actions: Action[] = []; actions: Action[] = [];
classes: Class[] = [];
constructor(component: Component, parent: Node, scope: TemplateScope, info: Element) { constructor(component: Component, parent: Node, scope: TemplateScope, info: Element) {
super(component, parent, scope, info); super(component, parent, scope, info);
@ -18,6 +20,8 @@ export default class Body extends Node {
this.handlers.push(new EventHandler(component, this, scope, node)); this.handlers.push(new EventHandler(component, this, scope, node));
} else if (node.type === 'Action') { } else if (node.type === 'Action') {
this.actions.push(new Action(component, this, scope, node)); this.actions.push(new Action(component, this, scope, node));
} else if (node.type === 'Class') {
this.classes.push(new Class(component, this, scope, node));
} else { } else {
// TODO there shouldn't be anything else here... // TODO there shouldn't be anything else here...
} }

@ -1,25 +1,66 @@
import Block from '../Block'; import Block from '../Block';
import Wrapper from './shared/Wrapper'; import Wrapper from './shared/Wrapper';
import { x } from 'code-red'; import { b, x } from 'code-red';
import Body from '../../nodes/Body'; import Body from '../../nodes/Body';
import { Identifier } from 'estree'; import { Node, Identifier } from 'estree';
import EventHandler from './Element/EventHandler'; import EventHandler from './Element/EventHandler';
import add_event_handlers from './shared/add_event_handlers'; import add_event_handlers from './shared/add_event_handlers';
import { TemplateNode } from '../../../interfaces'; import { TemplateNode } from '../../../interfaces';
import Renderer from '../Renderer'; import Renderer from '../Renderer';
import add_actions from './shared/add_actions'; import add_actions from './shared/add_actions';
import is_dynamic from './shared/is_dynamic';
export default class BodyWrapper extends Wrapper { export default class BodyWrapper extends Wrapper {
node: Body; node: Body;
handlers: EventHandler[]; handlers: EventHandler[];
class_dependencies: string[];
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: TemplateNode) { constructor(renderer: Renderer, block: Block, parent: Wrapper, node: TemplateNode) {
super(renderer, block, parent, node); super(renderer, block, parent, node);
this.handlers = this.node.handlers.map(handler => new EventHandler(handler, this)); this.handlers = this.node.handlers.map(handler => new EventHandler(handler, this));
this.class_dependencies = [];
}
add_classes(block: Block) {
this.node.classes.forEach(class_directive => {
const { expression, name } = class_directive;
let snippet: Node | string;
let dependencies: Set<string>;
if (expression) {
snippet = expression.manipulate(block);
dependencies = expression.dependencies;
} else {
snippet = name;
dependencies = new Set([name]);
}
const updater = b`@toggle_class(@_document.body, "${name}", ${snippet});`;
block.chunks.hydrate.push(updater);
if ((dependencies && dependencies.size > -1) || this.class_dependencies.length) {
const all_dependencies = this.class_dependencies.concat(...dependencies);
const condition = block.renderer.dirty(all_dependencies);
// If all of the dependencies are non-dynamic (don't get updated) then there is no reason
// to add an updater for this.
const any_dynamic_dependencies = all_dependencies.some((dep) => {
const variable = this.renderer.component.var_lookup.get(dep);
return !variable || is_dynamic(variable);
});
if (any_dynamic_dependencies) {
block.chunks.update.push(b`
if (${condition}) {
${updater}
}
`);
}
}
});
} }
render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) { render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) {
add_event_handlers(block, x`@_document.body`, this.handlers); add_event_handlers(block, x`@_document.body`, this.handlers);
add_actions(block, x`@_document.body`, this.node.actions); add_actions(block, x`@_document.body`, this.node.actions);
this.add_classes(block);
} }
} }

@ -0,0 +1,3 @@
export default {
html: '<body class="one"></body>'
};

@ -0,0 +1 @@
<svelte:body class:one={true} />

@ -0,0 +1,16 @@
export default {
props: {
foo: true,
bar: true
},
html: '<body class="foo bar"></body>',
test({ assert, component, target }) {
component.foo = false;
assert.htmlEqual(target.innerHTML, `
<body class="bar"></body>
`);
}
};

@ -0,0 +1,7 @@
<script>
export let foo;
export let bar;
export let unused;
</script>
<svelte:body class:foo class:bar class:unused />
Loading…
Cancel
Save