Skip to content

Commit 1770dc9

Browse files
committed
Feat: Add readonly binding focused
1 parent b91a67b commit 1770dc9

File tree

7 files changed

+112
-4
lines changed

7 files changed

+112
-4
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2727,7 +2727,9 @@ export const template_visitors = {
27272727
case 'checked':
27282728
call_expr = b.call(`$.bind_checked`, state.node, getter, setter);
27292729
break;
2730-
2730+
case 'focused':
2731+
call_expr = b.call(`$.bind_focused`, state.node, setter);
2732+
break;
27312733
case 'group': {
27322734
/** @type {import('estree').CallExpression[]} */
27332735
const indexes = [];

packages/svelte/src/compiler/phases/bindings.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export const binding_properties = {
2121
event: 'durationchange',
2222
omit_in_ssr: true
2323
},
24+
focused: {},
2425
paused: {
2526
valid_elements: ['audio', 'video'],
2627
omit_in_ssr: true

packages/svelte/src/internal/client/dom/elements/bindings/universal.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,31 @@ export function bind_property(property, event_name, type, element, get_value, up
6767
}
6868
});
6969
}
70+
71+
/**
72+
* @param {HTMLElement} element
73+
* @param {(value: unknown) => void} update
74+
* @returns {void}
75+
*/
76+
export function bind_focused(element, update) {
77+
var focus_handler = () => {
78+
update(true);
79+
}
80+
var blur_handler = () => {
81+
update(false);
82+
}
83+
84+
element.addEventListener('focus', focus_handler);
85+
element.addEventListener('blur', blur_handler);
86+
87+
/** @type {ReturnType<typeof setTimeout>} */
88+
89+
render_effect(() => {
90+
if(element === document.activeElement) {
91+
update(true)
92+
} else {
93+
update(false)
94+
}
95+
96+
});
97+
}

packages/svelte/src/internal/client/dom/elements/events.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ export function event(event_name, dom, handler, capture, passive) {
2525
return handler.call(this, event);
2626
}
2727
}
28-
2928
dom.addEventListener(event_name, target_handler, options);
3029

3130
// @ts-ignore
@@ -149,7 +148,6 @@ export function handle_event_propagation(handler_element, event) {
149148

150149
current_target = parent_element;
151150
}
152-
153151
// @ts-expect-error is used above
154152
event.__root = handler_element;
155153
// @ts-expect-error is used above

packages/svelte/src/internal/client/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export { bind_prop } from './dom/elements/bindings/props.js';
4444
export { bind_select_value, init_select, select_option } from './dom/elements/bindings/select.js';
4545
export { bind_element_size, bind_resize_observer } from './dom/elements/bindings/size.js';
4646
export { bind_this } from './dom/elements/bindings/this.js';
47-
export { bind_content_editable, bind_property } from './dom/elements/bindings/universal.js';
47+
export { bind_content_editable, bind_property, bind_focused } from './dom/elements/bindings/universal.js';
4848
export { bind_window_scroll, bind_window_size } from './dom/elements/bindings/window.js';
4949
export {
5050
once,
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { tick } from 'svelte';
2+
3+
import { ok, test } from '../../test';
4+
5+
export default test({
6+
html: `
7+
<div>false</div>
8+
<div>false</div>
9+
<input type="number">
10+
<input type="number">
11+
<button>click</button>
12+
`,
13+
14+
ssrHtml: `
15+
<div></div>
16+
<div></div>
17+
<input type="number">
18+
<input type="number">
19+
<button>click</button>
20+
`,
21+
22+
async test({ assert, component, target, window }) {
23+
const [d1, d2] = target.querySelectorAll('div');
24+
const [in1, in2] = target.querySelectorAll('input');
25+
const button = target.querySelector('button');
26+
ok(in1);
27+
ok(in2);
28+
ok(button);
29+
ok(d1);
30+
ok(d2);
31+
assert.equal(d1.textContent, 'false');
32+
assert.equal(d2.textContent, 'false');
33+
const event1 = new window.MouseEvent('click', { bubbles: true });
34+
in1.value = '1';
35+
await in1.dispatchEvent(event1);
36+
await tick();
37+
assert.equal(window.document.activeElement, in1);
38+
assert.equal(component.a, true);
39+
assert.equal(component.b, false);
40+
assert.equal(d1.textContent, 'true');
41+
assert.equal(d2.textContent, 'false');
42+
43+
in2.value = '1';
44+
const event2 = new window.MouseEvent('click', { bubbles: true });
45+
await in2.dispatchEvent(event2);
46+
await tick();
47+
assert.equal(component.a, false);
48+
assert.equal(component.b, true);
49+
assert.equal(d1.textContent, 'false');
50+
assert.equal(d2.textContent, 'true');
51+
52+
const event3 = new window.MouseEvent('click', { bubbles: true });
53+
await button.dispatchEvent(event3);
54+
await tick();
55+
56+
assert.equal(d1.textContent, 'false');
57+
assert.equal(d2.textContent, 'false');
58+
59+
assert.htmlEqual(
60+
target.innerHTML,
61+
`
62+
<div>false</div>
63+
<div>false</div>
64+
<input type="number">
65+
<input type="number">
66+
<button>click</button>
67+
`
68+
);
69+
}
70+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
export let a;
3+
export let b;
4+
</script>
5+
<div>{a}</div>
6+
<div>{b}</div>
7+
<input type="number" bind:focused={a} onclick='{e => e.target.focus()}' />
8+
<input type="number" bind:focused={b} onclick='{e => e.target.focus()}' />
9+
<button onclick='{e => e.target.focus()}'>click</button>

0 commit comments

Comments
 (0)