diff --git a/.changeset/thick-swans-type.md b/.changeset/thick-swans-type.md new file mode 100644 index 000000000000..430cdbdc5b7c --- /dev/null +++ b/.changeset/thick-swans-type.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +feat: add read-only `bind:focused` diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index fa711c324f12..e9d3b519c724 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -2727,7 +2727,9 @@ export const template_visitors = { case 'checked': call_expr = b.call(`$.bind_checked`, state.node, getter, setter); break; - + case 'focused': + call_expr = b.call(`$.bind_focused`, state.node, setter); + break; case 'group': { /** @type {import('estree').CallExpression[]} */ const indexes = []; diff --git a/packages/svelte/src/compiler/phases/bindings.js b/packages/svelte/src/compiler/phases/bindings.js index e3fdd7eb8c07..9a6f1c316b2f 100644 --- a/packages/svelte/src/compiler/phases/bindings.js +++ b/packages/svelte/src/compiler/phases/bindings.js @@ -21,6 +21,7 @@ export const binding_properties = { event: 'durationchange', omit_in_ssr: true }, + focused: {}, paused: { valid_elements: ['audio', 'video'], omit_in_ssr: true diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/universal.js b/packages/svelte/src/internal/client/dom/elements/bindings/universal.js index 8c40c04da5e3..f42f67d59472 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/universal.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/universal.js @@ -1,4 +1,5 @@ import { render_effect } from '../../../reactivity/effects.js'; +import { listen } from './shared.js'; /** * @param {'innerHTML' | 'textContent' | 'innerText'} property @@ -67,3 +68,14 @@ export function bind_property(property, event_name, type, element, get_value, up } }); } + +/** + * @param {HTMLElement} element + * @param {(value: unknown) => void} update + * @returns {void} + */ +export function bind_focused(element, update) { + listen(element, ['focus', 'blur'], () => { + update(element === document.activeElement); + }); +} diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 2362698ab183..6e6488c0019f 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -44,7 +44,11 @@ export { bind_prop } from './dom/elements/bindings/props.js'; export { bind_select_value, init_select, select_option } from './dom/elements/bindings/select.js'; export { bind_element_size, bind_resize_observer } from './dom/elements/bindings/size.js'; export { bind_this } from './dom/elements/bindings/this.js'; -export { bind_content_editable, bind_property } from './dom/elements/bindings/universal.js'; +export { + bind_content_editable, + bind_property, + bind_focused +} from './dom/elements/bindings/universal.js'; export { bind_window_scroll, bind_window_size } from './dom/elements/bindings/window.js'; export { once, diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-focused/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-focused/_config.js new file mode 100644 index 000000000000..02f2574f340e --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/binding-focused/_config.js @@ -0,0 +1,19 @@ +import { flushSync } from 'svelte'; + +import { test } from '../../test'; + +export default test({ + async test({ assert, component, target, window }) { + const [in1, in2] = target.querySelectorAll('input'); + + flushSync(() => in1.focus()); + assert.equal(window.document.activeElement, in1); + assert.equal(component.a, true); + assert.equal(component.b, false); + + flushSync(() => in2.focus()); + assert.equal(window.document.activeElement, in2); + assert.equal(component.a, false); + assert.equal(component.b, true); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-focused/main.svelte b/packages/svelte/tests/runtime-legacy/samples/binding-focused/main.svelte new file mode 100644 index 000000000000..7ba892393dc7 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/binding-focused/main.svelte @@ -0,0 +1,7 @@ + + + +