Skip to content

Commit 7578af3

Browse files
authored
fix: retain style directive value after style attribute is updated (#7610)
fixes #7475
1 parent a6c329f commit 7578af3

File tree

4 files changed

+85
-16
lines changed
  • src/compiler/compile/render_dom/wrappers/Element
  • test

4 files changed

+85
-16
lines changed

src/compiler/compile/render_dom/wrappers/Element/index.ts

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ export default class ElementWrapper extends Wrapper {
160160
bindings: Binding[];
161161
event_handlers: EventHandler[];
162162
class_dependencies: string[];
163+
dynamic_style_dependencies: Set<string>;
163164
has_dynamic_attribute: boolean;
164165

165166
select_binding_dependencies?: Set<string>;
@@ -214,6 +215,8 @@ export default class ElementWrapper extends Wrapper {
214215
return;
215216
}
216217

218+
this.dynamic_style_dependencies = new Set();
219+
217220
if (this.node.children.length) {
218221
this.node.lets.forEach(l => {
219222
extract_names(l.value || l.name).forEach(name => {
@@ -801,11 +804,13 @@ export default class ElementWrapper extends Wrapper {
801804
}
802805

803806
add_attributes(block: Block) {
804-
// Get all the class dependencies first
807+
// Get all the class and style dependencies first
805808
this.attributes.forEach((attribute) => {
806809
if (attribute.node.name === 'class') {
807810
const dependencies = attribute.node.get_dependencies();
808811
push_array(this.class_dependencies, dependencies);
812+
} else if (attribute.node.name === 'style') {
813+
add_to_set(this.dynamic_style_dependencies, attribute.node.get_dependencies());
809814
}
810815
});
811816

@@ -1170,8 +1175,18 @@ export default class ElementWrapper extends Wrapper {
11701175

11711176
add_styles(block: Block) {
11721177
const has_spread = this.node.attributes.some(attr => attr.is_spread);
1178+
1179+
let style_changed_var: Identifier | undefined;
1180+
const maybe_create_style_changed_var = () => {
1181+
if (!style_changed_var && this.dynamic_style_dependencies.size) {
1182+
style_changed_var = block.get_unique_name('style_changed');
1183+
const style_attr_dirty = block.renderer.dirty([...this.dynamic_style_dependencies]);
1184+
block.chunks.update.push(b`const ${style_changed_var} = ${style_attr_dirty};`);
1185+
}
1186+
};
1187+
11731188
this.node.styles.forEach((style_directive) => {
1174-
const { name, expression, should_cache, important } = style_directive;
1189+
const { name, expression, important, should_cache } = style_directive;
11751190

11761191
const snippet = expression.manipulate(block);
11771192
let cached_snippet: Identifier | undefined;
@@ -1184,24 +1199,40 @@ export default class ElementWrapper extends Wrapper {
11841199

11851200
block.chunks.hydrate.push(updater);
11861201

1187-
const dependencies = expression.dynamic_dependencies();
1202+
// Assume that style has changed through the spread attribute
11881203
if (has_spread) {
11891204
block.chunks.update.push(updater);
1190-
} else if (dependencies.length > 0) {
1205+
} else {
1206+
const self_deps = expression.dynamic_dependencies();
1207+
const all_deps = new Set([
1208+
...self_deps,
1209+
...this.dynamic_style_dependencies
1210+
]);
1211+
1212+
if (all_deps.size === 0) return;
1213+
1214+
let condition = block.renderer.dirty([...all_deps]);
1215+
11911216
if (should_cache) {
1192-
block.chunks.update.push(b`
1193-
if (${block.renderer.dirty(dependencies)} && (${cached_snippet} !== (${cached_snippet} = ${snippet}))) {
1194-
${updater}
1195-
}
1196-
`);
1197-
} else {
1198-
block.chunks.update.push(b`
1199-
if (${block.renderer.dirty(dependencies)}) {
1200-
${updater}
1201-
}
1202-
`);
1217+
condition = x`${condition} && ${cached_snippet} !== (${cached_snippet} = ${snippet})`;
12031218
}
1219+
1220+
if (this.dynamic_style_dependencies.size > 0) {
1221+
maybe_create_style_changed_var();
1222+
// If all dependencies are same as the style attribute dependencies, then we can skip the dirty check
1223+
condition =
1224+
all_deps.size === this.dynamic_style_dependencies.size
1225+
? style_changed_var
1226+
: x`${style_changed_var} || ${condition}`;
1227+
}
1228+
1229+
block.chunks.update.push(b`
1230+
if (${condition}) {
1231+
${updater}
1232+
}
1233+
`);
12041234
}
1235+
12051236
});
12061237
}
12071238

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export default {
2+
html: `
3+
<p style="font-size: 32px; color: red; background-color: green; border-color: green;"></p>
4+
`,
5+
6+
test({ assert, target, window, component }) {
7+
const p = target.querySelector('p');
8+
const styles = window.getComputedStyle(p);
9+
assert.equal(styles.color, 'rgb(255, 0, 0)');
10+
assert.equal(styles.fontSize, '32px');
11+
assert.equal(styles.backgroundColor, 'rgb(0, 128, 0)');
12+
assert.equal(styles.borderColor, 'rgb(0, 128, 0)');
13+
14+
component.foo = 'font-size: 50px; color: green;'; // Update style attribute
15+
{
16+
const p = target.querySelector('p');
17+
const styles = window.getComputedStyle(p);
18+
assert.equal(styles.color, 'rgb(255, 0, 0)');
19+
assert.equal(styles.fontSize, '32px');
20+
assert.equal(styles.backgroundColor, 'rgb(0, 128, 0)');
21+
assert.equal(styles.borderColor, 'rgb(0, 128, 0)');
22+
}
23+
}
24+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script>
2+
export let foo = "font-size: 20px; color: blue;";
3+
let baz = "red"; // static value
4+
let bar = "32"; // static value interpolated
5+
export let bg = "gre"; // dynamic value interpolated/cached
6+
export let borderColor = "green"; // dynamic value non-cached
7+
</script>
8+
9+
<p
10+
style:font-size="{bar}px"
11+
style:color={baz}
12+
style="{foo}"
13+
style:background-color="{bg}en"
14+
style:border-color={borderColor}
15+
/>

test/runtime/samples/inline-style-directive-and-style-attr-merged/_config.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ export default {
55

66
test({ assert, target, window }) {
77
const p = target.querySelector('p');
8-
98
const styles = window.getComputedStyle(p);
109
assert.equal(styles.color, 'red');
1110
assert.equal(styles.height, '40px');

0 commit comments

Comments
 (0)