diff --git a/src/generators/nodes/Attribute.ts b/src/generators/nodes/Attribute.ts index 01b62d23da..08075efee2 100644 --- a/src/generators/nodes/Attribute.ts +++ b/src/generators/nodes/Attribute.ts @@ -540,6 +540,7 @@ const attributeLookup = { 'textarea', ], }, + volume: { appliesTo: ['audio', 'video'] }, width: { appliesTo: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'], }, diff --git a/src/generators/nodes/Binding.ts b/src/generators/nodes/Binding.ts index 3ab5f79769..f1725a6d73 100644 --- a/src/generators/nodes/Binding.ts +++ b/src/generators/nodes/Binding.ts @@ -73,9 +73,11 @@ export default class Binding extends Node { ); } - if (this.name === 'currentTime') { + if (this.name === 'currentTime' || this.name === 'volume') { updateCondition = `!isNaN(${snippet})`; - initialUpdate = null; + + if (this.name === 'currentTime') + initialUpdate = null; } if (this.name === 'paused') { @@ -267,4 +269,4 @@ function isComputed(node: Node) { } return false; -} \ No newline at end of file +} diff --git a/src/generators/nodes/Element.ts b/src/generators/nodes/Element.ts index d2b8ba658b..48852c27ea 100644 --- a/src/generators/nodes/Element.ts +++ b/src/generators/nodes/Element.ts @@ -760,5 +760,11 @@ const events = [ filter: (node: Element, name: string) => node.isMediaNode() && (name === 'buffered' || name === 'seekable') + }, + { + eventNames: ['volumechange'], + filter: (node: Element, name: string) => + node.isMediaNode() && + name === 'volume' } ]; diff --git a/src/validate/html/validateElement.ts b/src/validate/html/validateElement.ts index 1156ebcea7..550f8d2ae7 100644 --- a/src/validate/html/validateElement.ts +++ b/src/validate/html/validateElement.ts @@ -139,7 +139,8 @@ export default function validateElement( name === 'paused' || name === 'buffered' || name === 'seekable' || - name === 'played' + name === 'played' || + name === 'volume' ) { if (node.name !== 'audio' && node.name !== 'video') { validator.error( diff --git a/test/js/samples/media-bindings/expected-bundle.js b/test/js/samples/media-bindings/expected-bundle.js index fdf8a4f755..bbd56d8123 100644 --- a/test/js/samples/media-bindings/expected-bundle.js +++ b/test/js/samples/media-bindings/expected-bundle.js @@ -226,6 +226,12 @@ function create_main_fragment(state, component) { component.set({ buffered: timeRangesToArray(audio.buffered), seekable: timeRangesToArray(audio.seekable) }); } + function audio_volumechange_handler() { + audio_updating = true; + component.set({ volume: audio.volume }); + audio_updating = false; + } + return { c: function create() { audio = createElement("audio"); @@ -243,15 +249,19 @@ function create_main_fragment(state, component) { if (!('buffered' in state)) component.root._beforecreate.push(audio_progress_handler); addListener(audio, "loadedmetadata", audio_loadedmetadata_handler); if (!('buffered' in state && 'seekable' in state)) component.root._beforecreate.push(audio_loadedmetadata_handler); + addListener(audio, "volumechange", audio_volumechange_handler); }, m: function mount(target, anchor) { insertNode(audio, target, anchor); + + audio.volume = state.volume; }, p: function update(changed, state) { if (!audio_updating && !isNaN(state.currentTime )) audio.currentTime = state.currentTime ; - if (!audio_updating && audio_is_paused !== (audio_is_paused = state.paused)) audio[audio_is_paused ? "pause" : "play"](); + if (!audio_updating && audio_is_paused !== (audio_is_paused = state.paused )) audio[audio_is_paused ? "pause" : "play"](); + if (!audio_updating && !isNaN(state.volume)) audio.volume = state.volume; }, u: function unmount() { @@ -265,6 +275,7 @@ function create_main_fragment(state, component) { removeListener(audio, "pause", audio_play_pause_handler); removeListener(audio, "progress", audio_progress_handler); removeListener(audio, "loadedmetadata", audio_loadedmetadata_handler); + removeListener(audio, "volumechange", audio_volumechange_handler); } }; } diff --git a/test/js/samples/media-bindings/expected.js b/test/js/samples/media-bindings/expected.js index c1dd10c238..ae80f288f6 100644 --- a/test/js/samples/media-bindings/expected.js +++ b/test/js/samples/media-bindings/expected.js @@ -30,6 +30,12 @@ function create_main_fragment(state, component) { component.set({ buffered: timeRangesToArray(audio.buffered), seekable: timeRangesToArray(audio.seekable) }); } + function audio_volumechange_handler() { + audio_updating = true; + component.set({ volume: audio.volume }); + audio_updating = false; + } + return { c: function create() { audio = createElement("audio"); @@ -47,15 +53,19 @@ function create_main_fragment(state, component) { if (!('buffered' in state)) component.root._beforecreate.push(audio_progress_handler); addListener(audio, "loadedmetadata", audio_loadedmetadata_handler); if (!('buffered' in state && 'seekable' in state)) component.root._beforecreate.push(audio_loadedmetadata_handler); + addListener(audio, "volumechange", audio_volumechange_handler); }, m: function mount(target, anchor) { insertNode(audio, target, anchor); + + audio.volume = state.volume; }, p: function update(changed, state) { if (!audio_updating && !isNaN(state.currentTime )) audio.currentTime = state.currentTime ; - if (!audio_updating && audio_is_paused !== (audio_is_paused = state.paused)) audio[audio_is_paused ? "pause" : "play"](); + if (!audio_updating && audio_is_paused !== (audio_is_paused = state.paused )) audio[audio_is_paused ? "pause" : "play"](); + if (!audio_updating && !isNaN(state.volume)) audio.volume = state.volume; }, u: function unmount() { @@ -69,6 +79,7 @@ function create_main_fragment(state, component) { removeListener(audio, "pause", audio_play_pause_handler); removeListener(audio, "progress", audio_progress_handler); removeListener(audio, "loadedmetadata", audio_loadedmetadata_handler); + removeListener(audio, "volumechange", audio_volumechange_handler); } }; } diff --git a/test/js/samples/media-bindings/input.html b/test/js/samples/media-bindings/input.html index 07a5cc1bc1..e7fe359341 100644 --- a/test/js/samples/media-bindings/input.html +++ b/test/js/samples/media-bindings/input.html @@ -1 +1 @@ -