Skip to content

Commit e73b9ae

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

File tree

7 files changed

+114
-4
lines changed

7 files changed

+114
-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: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { tick } from 'svelte';
2+
3+
import { ok, test } from '../../test';
4+
5+
export default test({
6+
7+
html: `
8+
<div>false</div>
9+
<div>false</div>
10+
<input type="number">
11+
<input type="number">
12+
<button>click</button>
13+
`,
14+
15+
ssrHtml: `
16+
<div></div>
17+
<div></div>
18+
<input type="number">
19+
<input type="number">
20+
<button>click</button>
21+
`,
22+
23+
async test({ assert, component, target, window }) {
24+
const [d1, d2] = target.querySelectorAll('div')
25+
const [in1, in2] = target.querySelectorAll('input');
26+
const button = target.querySelector('button');
27+
ok(in1);
28+
ok(in2);
29+
ok(button);
30+
ok(d1);
31+
ok(d2);
32+
assert.equal(d1.textContent, 'false');
33+
assert.equal(d2.textContent, 'false');
34+
const event1 = new window.MouseEvent('click', { bubbles: true })
35+
in1.value = '1';
36+
await in1.dispatchEvent(event1);
37+
await tick();
38+
assert.equal(window.document.activeElement, in1)
39+
assert.equal(component.a, true);
40+
assert.equal(component.b, false);
41+
assert.equal(d1.textContent, 'true');
42+
assert.equal(d2.textContent, 'false');
43+
44+
45+
in2.value = '1';
46+
const event2 = new window.MouseEvent('click', { bubbles: true })
47+
await in2.dispatchEvent(event2);
48+
await tick();
49+
assert.equal(component.a, false);
50+
assert.equal(component.b, true);
51+
assert.equal(d1.textContent, 'false');
52+
assert.equal(d2.textContent, 'true');
53+
54+
const event3 = new window.MouseEvent('click', { bubbles: true })
55+
await button.dispatchEvent(event3);
56+
await tick();
57+
58+
assert.equal(d1.textContent, 'false');
59+
assert.equal(d2.textContent, 'false')
60+
61+
assert.htmlEqual(
62+
target.innerHTML,
63+
`
64+
<div>false</div>
65+
<div>false</div>
66+
<input type="number">
67+
<input type="number">
68+
<button>click</button>
69+
`
70+
);
71+
}
72+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
let a;
3+
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)