Skip to content

Commit 3864229

Browse files
fix: separate template_effect for dynamic class/style directive with dynamic attributes (#13171)
* fix: separate `template_effect` for dynamic class/style directive with dynamic attributes * fix: only move to `init` if it `has_call` * fix: initialize spread `needs_isolation` based on if there are directives with `has_call` * fix: revert splitting templates and generate deriveds instead * small tweaks --------- Co-authored-by: Rich Harris <[email protected]>
1 parent 6b69de7 commit 3864229

File tree

4 files changed

+69
-6
lines changed

4 files changed

+69
-6
lines changed

.changeset/nasty-eggs-walk.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: separate `template_effect` for dynamic class/style directive with dynamic attributes

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

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
/** @import { ComponentContext } from '../../types' */
44
import { normalize_attribute } from '../../../../../../utils.js';
55
import * as b from '../../../../../utils/builders.js';
6-
import { build_getter } from '../../utils.js';
6+
import { build_getter, create_derived } from '../../utils.js';
77
import { build_template_literal, build_update } from './utils.js';
88

99
/**
@@ -25,11 +25,20 @@ export function build_style_directives(
2525
const state = context.state;
2626

2727
for (const directive of style_directives) {
28+
const { has_state, has_call } = directive.metadata.expression;
29+
2830
let value =
2931
directive.value === true
3032
? build_getter({ name: directive.name, type: 'Identifier' }, context.state)
3133
: build_attribute_value(directive.value, context).value;
3234

35+
if (has_call) {
36+
const id = b.id(state.scope.generate('style_directive'));
37+
38+
state.init.push(b.const(id, create_derived(state, b.thunk(value))));
39+
value = b.call('$.get', id);
40+
}
41+
3342
const update = b.stmt(
3443
b.call(
3544
'$.set_style',
@@ -41,8 +50,6 @@ export function build_style_directives(
4150
)
4251
);
4352

44-
const { has_state, has_call } = directive.metadata.expression;
45-
4653
if (!is_attributes_reactive && has_call) {
4754
state.init.push(build_update(update));
4855
} else if (is_attributes_reactive || has_state || has_call) {
@@ -69,10 +76,17 @@ export function build_class_directives(
6976
) {
7077
const state = context.state;
7178
for (const directive of class_directives) {
72-
const value = /** @type {Expression} */ (context.visit(directive.expression));
73-
const update = b.stmt(b.call('$.toggle_class', element_id, b.literal(directive.name), value));
74-
7579
const { has_state, has_call } = directive.metadata.expression;
80+
let value = /** @type {Expression} */ (context.visit(directive.expression));
81+
82+
if (has_call) {
83+
const id = b.id(state.scope.generate('class_directive'));
84+
85+
state.init.push(b.const(id, create_derived(state, b.thunk(value))));
86+
value = b.call('$.get', id);
87+
}
88+
89+
const update = b.stmt(b.call('$.toggle_class', element_id, b.literal(directive.name), value));
7690

7791
if (!is_attributes_reactive && has_call) {
7892
state.init.push(build_update(update));
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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, ['called', 'called']);
10+
11+
// this is to assert that the order of the attributes is still not relevant
12+
// and directives take precedence over generic attribute
13+
assert.equal(div.classList.contains('dark'), false);
14+
assert.equal(div2.style.color, 'red');
15+
16+
flushSync(() => button?.click());
17+
assert.deepEqual(logs, ['called', 'called']);
18+
}
19+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<script>
2+
let value = $state(0);
3+
4+
function dark(){
5+
console.log('called')
6+
return false;
7+
}
8+
9+
function get_class(){
10+
return 'dark';
11+
}
12+
13+
function color(){
14+
console.log('called')
15+
return 'red';
16+
}
17+
18+
function get_style(){
19+
return 'color: green';
20+
}
21+
</script>
22+
23+
<div class:dark={dark()} class={get_class()}></div>
24+
<div style:color={color()} style={get_style()}></div>
25+
<button onclick={()=> value++}>{value}</button>

0 commit comments

Comments
 (0)