Skip to content

Commit 8148e63

Browse files
fix: apply class/style directives after attributes (#13535)
fixes #13270 by resetting the has_call boolean to false to style/class attributes, which means we're not creating a separate template effect for the attribute, instead they're added to the common template effect in which style/class directives are also added to later
1 parent 2b0741f commit 8148e63

File tree

4 files changed

+82
-1
lines changed

4 files changed

+82
-1
lines changed

.changeset/eighty-hornets-breathe.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: apply class/style directives after attributes

packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
import * as b from '../../../../utils/builders.js';
2020
import { is_custom_element_node } from '../../../nodes.js';
2121
import { clean_nodes, determine_namespace_for_children } from '../../utils.js';
22-
import { build_getter, can_inline_variable } from '../utils.js';
22+
import { build_getter, can_inline_variable, create_derived } from '../utils.js';
2323
import {
2424
get_attribute_name,
2525
build_attribute_value,
@@ -534,6 +534,14 @@ function build_element_attribute_update_assignment(element, node_id, attribute,
534534
let update;
535535

536536
if (name === 'class') {
537+
if (attribute.metadata.expression.has_state && has_call) {
538+
// ensure we're not creating a separate template effect for this so that
539+
// potential class directives are added to the same effect and therefore always apply
540+
const id = b.id(state.scope.generate('class_derived'));
541+
state.init.push(b.const(id, create_derived(state, b.thunk(value))));
542+
value = b.call('$.get', id);
543+
has_call = false;
544+
}
537545
update = b.stmt(
538546
b.call(
539547
is_svg ? '$.set_svg_class' : is_mathml ? '$.set_mathml_class' : '$.set_class',
@@ -548,6 +556,14 @@ function build_element_attribute_update_assignment(element, node_id, attribute,
548556
} else if (is_dom_property(name)) {
549557
update = b.stmt(b.assignment('=', b.member(node_id, name), value));
550558
} else {
559+
if (name === 'style' && attribute.metadata.expression.has_state && has_call) {
560+
// ensure we're not creating a separate template effect for this so that
561+
// potential style directives are added to the same effect and therefore always apply
562+
const id = b.id(state.scope.generate('style_derived'));
563+
state.init.push(b.const(id, create_derived(state, b.thunk(value))));
564+
value = b.call('$.get', id);
565+
has_call = false;
566+
}
551567
const callee = name.startsWith('xlink') ? '$.set_xlink_attribute' : '$.set_attribute';
552568
update = b.stmt(
553569
b.call(
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
test({ target, logs, assert }) {
6+
const [div, div2] = target.querySelectorAll('div');
7+
const button = target.querySelector('button');
8+
9+
assert.deepEqual(logs, [
10+
'updated class attribute',
11+
'updated class directive',
12+
'updated style attribute',
13+
'updated style directive'
14+
]);
15+
16+
assert.ok(div.classList.contains('dark'));
17+
assert.ok(div.classList.contains('small'));
18+
19+
assert.equal(div2.getAttribute('style'), 'background: green; color: green;');
20+
21+
flushSync(() => button?.click());
22+
23+
assert.deepEqual(logs, [
24+
'updated class attribute',
25+
'updated class directive',
26+
'updated style attribute',
27+
'updated style directive',
28+
'updated class attribute',
29+
'updated style attribute'
30+
]);
31+
32+
assert.ok(div.classList.contains('dark'));
33+
assert.ok(div.classList.contains('big'));
34+
35+
assert.equal(div2.getAttribute('style'), 'background: red; color: green;');
36+
}
37+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<script>
2+
let value = $state(0);
3+
function dark(){
4+
console.log('updated class directive');
5+
return true;
6+
}
7+
function get_class(){
8+
console.log('updated class attribute');
9+
return value % 2 ? 'big' : 'small';
10+
}
11+
function color(){
12+
console.log('updated style directive');
13+
return "green";
14+
}
15+
function get_style(){
16+
console.log('updated style attribute');
17+
return value % 2 ? 'background: red' : 'background: green';
18+
}
19+
</script>
20+
21+
<div class:dark={dark()} class={get_class()}></div>
22+
<div style:color={color()} style={get_style()}></div>
23+
<button onclick={()=> value++}>switch</button>

0 commit comments

Comments
 (0)