fix: `muted` reactive without `bind` and select/autofocus attributes … (#15326)

* fix: `muted` reactive without `bind` and select/autofocus attributes working with function calls

Co-authored-by: Joel Howse <joel@helicalco.com>

* chore: add comments

---------

Co-authored-by: Joel Howse <joel@helicalco.com>
pull/15304/head
Paolo Ricciuti 7 months ago committed by GitHub
parent 9091e7e930
commit d8e78f7f57
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: `muted` reactive without `bind` and select/autofocus attributes working with function calls

@ -29,7 +29,8 @@ import {
build_render_statement,
build_template_chunk,
build_update_assignment,
get_expression_id
get_expression_id,
memoize_expression
} from './shared/utils.js';
import { visit_event_attribute } from './shared/events.js';
@ -532,18 +533,30 @@ function build_element_attribute_update_assignment(
const is_svg = context.state.metadata.namespace === 'svg' || element.name === 'svg';
const is_mathml = context.state.metadata.namespace === 'mathml';
const is_autofocus = name === 'autofocus';
let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) =>
metadata.has_call ? get_expression_id(state, value) : value
metadata.has_call
? // if it's autofocus we will not add this to a template effect so we don't want to get the expression id
// but separately memoize the expression
is_autofocus
? memoize_expression(state, value)
: get_expression_id(state, value)
: value
);
if (name === 'autofocus') {
if (is_autofocus) {
state.init.push(b.stmt(b.call('$.autofocus', node_id, value)));
return false;
}
// Special case for Firefox who needs it set as a property in order to work
if (name === 'muted') {
state.init.push(b.stmt(b.assignment('=', b.member(node_id, b.id('muted')), value)));
if (!has_state) {
state.init.push(b.stmt(b.assignment('=', b.member(node_id, b.id('muted')), value)));
return false;
}
state.update.push(b.stmt(b.assignment('=', b.member(node_id, b.id('muted')), value)));
return false;
}
@ -660,8 +673,18 @@ function build_custom_element_attribute_update_assignment(node_id, attribute, co
*/
function build_element_special_value_attribute(element, node_id, attribute, context) {
const state = context.state;
const is_select_with_value =
// attribute.metadata.dynamic would give false negatives because even if the value does not change,
// the inner options could still change, so we need to always treat it as reactive
element === 'select' && attribute.value !== true && !is_text_attribute(attribute);
const { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) =>
metadata.has_call ? get_expression_id(state, value) : value
metadata.has_call
? // if is a select with value we will also invoke `init_select` which need a reference before the template effect so we memoize separately
is_select_with_value
? memoize_expression(context.state, value)
: get_expression_id(state, value)
: value
);
const inner_assignment = b.assignment(
@ -674,11 +697,6 @@ function build_element_special_value_attribute(element, node_id, attribute, cont
)
);
const is_select_with_value =
// attribute.metadata.dynamic would give false negatives because even if the value does not change,
// the inner options could still change, so we need to always treat it as reactive
element === 'select' && attribute.value !== true && !is_text_attribute(attribute);
const update = b.stmt(
is_select_with_value
? b.sequence([

@ -0,0 +1,7 @@
import { test } from '../../test';
export default test({
async test({ assert, errors }) {
assert.deepEqual(errors, []);
}
});

@ -0,0 +1,6 @@
<svelte:options runes />
<script>
function test(){}
</script>
<input autofocus={test()} />

@ -0,0 +1,13 @@
import { flushSync } from 'svelte';
import { ok, test } from '../../test';
export default test({
async test({ assert, target, logs }) {
const btn = target.querySelector('button');
ok(btn);
flushSync(() => {
btn.click();
});
assert.deepEqual(logs, [true]);
}
});

@ -0,0 +1,13 @@
<script>
let muted = $state(false);
function volume_change(node){
node.addEventListener("volumechange", () => {
console.log(node.muted);
});
}
</script>
<audio use:volume_change {muted}></audio>
<button onclick={() => (muted = !muted)}></button>

@ -0,0 +1,7 @@
import { test } from '../../test';
export default test({
async test({ assert, errors }) {
assert.deepEqual(errors, []);
}
});

@ -0,0 +1,6 @@
<svelte:options runes />
<script>
function test(){}
</script>
<select value={test()}></select>
Loading…
Cancel
Save