feat: add stopImmediatePropagation event modifier (#8341)

Closes #5085

---------

Co-authored-by: Marcin Wicha <23581770+marcin-wicha@users.noreply.github.com>
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
pull/8342/head
Tan Li Hau 2 years ago committed by GitHub
parent acbf8135a8
commit c611f318d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -539,6 +539,7 @@ The following modifiers are available:
* `preventDefault` — calls `event.preventDefault()` before running the handler * `preventDefault` — calls `event.preventDefault()` before running the handler
* `stopPropagation` — calls `event.stopPropagation()`, preventing the event reaching the next element * `stopPropagation` — calls `event.stopPropagation()`, preventing the event reaching the next element
* `stopImmediatePropagation` - calls `event.stopImmediatePropagation()`, preventing other listeners of the same event from being fired.
* `passive` — improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so) * `passive` — improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so)
* `nonpassive` — explicitly set `passive: false` * `nonpassive` — explicitly set `passive: false`
* `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase * `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase

@ -183,6 +183,7 @@ const invisible_elements = new Set(['meta', 'html', 'script', 'style']);
const valid_modifiers = new Set([ const valid_modifiers = new Set([
'preventDefault', 'preventDefault',
'stopPropagation', 'stopPropagation',
'stopImmediatePropagation',
'capture', 'capture',
'once', 'once',
'passive', 'passive',

@ -41,6 +41,7 @@ export default class EventHandlerWrapper {
if (this.node.modifiers.has('preventDefault')) snippet = x`@prevent_default(${snippet})`; if (this.node.modifiers.has('preventDefault')) snippet = x`@prevent_default(${snippet})`;
if (this.node.modifiers.has('stopPropagation')) snippet = x`@stop_propagation(${snippet})`; if (this.node.modifiers.has('stopPropagation')) snippet = x`@stop_propagation(${snippet})`;
if (this.node.modifiers.has('stopImmediatePropagation')) snippet = x`@stop_immediate_propagation(${snippet})`;
if (this.node.modifiers.has('self')) snippet = x`@self(${snippet})`; if (this.node.modifiers.has('self')) snippet = x`@self(${snippet})`;
if (this.node.modifiers.has('trusted')) snippet = x`@trusted(${snippet})`; if (this.node.modifiers.has('trusted')) snippet = x`@trusted(${snippet})`;
@ -64,6 +65,7 @@ export default class EventHandlerWrapper {
if (block.renderer.options.dev) { if (block.renderer.options.dev) {
args.push(this.node.modifiers.has('preventDefault') ? TRUE : FALSE); args.push(this.node.modifiers.has('preventDefault') ? TRUE : FALSE);
args.push(this.node.modifiers.has('stopPropagation') ? TRUE : FALSE); args.push(this.node.modifiers.has('stopPropagation') ? TRUE : FALSE);
args.push(this.node.modifiers.has('stopImmediatePropagation') ? TRUE : FALSE);
} }
block.event_listeners.push( block.event_listeners.push(

@ -49,10 +49,11 @@ export function detach_after_dev(before: Node) {
} }
} }
export function listen_dev(node: Node, event: string, handler: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | EventListenerOptions, has_prevent_default?: boolean, has_stop_propagation?: boolean) { export function listen_dev(node: Node, event: string, handler: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | EventListenerOptions, has_prevent_default?: boolean, has_stop_propagation?: boolean, has_stop_immediate_propagation?: boolean) {
const modifiers = options === true ? [ 'capture' ] : options ? Array.from(Object.keys(options)) : []; const modifiers = options === true ? [ 'capture' ] : options ? Array.from(Object.keys(options)) : [];
if (has_prevent_default) modifiers.push('preventDefault'); if (has_prevent_default) modifiers.push('preventDefault');
if (has_stop_propagation) modifiers.push('stopPropagation'); if (has_stop_propagation) modifiers.push('stopPropagation');
if (has_stop_immediate_propagation) modifiers.push('stopImmediatePropagation');
dispatch_dev('SvelteDOMAddEventListener', { node, event, handler, modifiers }); dispatch_dev('SvelteDOMAddEventListener', { node, event, handler, modifiers });

@ -275,6 +275,14 @@ export function stop_propagation(fn) {
}; };
} }
export function stop_immediate_propagation(fn) {
return function (event) {
event.stopImmediatePropagation();
// @ts-ignore
return fn.call(this, event);
};
}
export function self(fn) { export function self(fn) {
return function(event) { return function(event) {
// @ts-ignore // @ts-ignore

@ -12,6 +12,7 @@ import {
run_all, run_all,
safe_not_equal, safe_not_equal,
space, space,
stop_immediate_propagation,
stop_propagation stop_propagation
} from "svelte/internal"; } from "svelte/internal";
@ -24,6 +25,8 @@ function create_fragment(ctx) {
let button1; let button1;
let t5; let t5;
let button2; let button2;
let t7;
let button3;
let mounted; let mounted;
let dispose; let dispose;
@ -41,6 +44,9 @@ function create_fragment(ctx) {
t5 = space(); t5 = space();
button2 = element("button"); button2 = element("button");
button2.textContent = "or me!"; button2.textContent = "or me!";
t7 = space();
button3 = element("button");
button3.textContent = "or me!";
}, },
m(target, anchor) { m(target, anchor) {
insert(target, div1, anchor); insert(target, div1, anchor);
@ -51,6 +57,8 @@ function create_fragment(ctx) {
append(div1, button1); append(div1, button1);
append(div1, t5); append(div1, t5);
append(div1, button2); append(div1, button2);
append(div1, t7);
append(div1, button3);
if (!mounted) { if (!mounted) {
dispose = [ dispose = [
@ -58,6 +66,8 @@ function create_fragment(ctx) {
listen(button0, "click", stop_propagation(prevent_default(handleClick))), listen(button0, "click", stop_propagation(prevent_default(handleClick))),
listen(button1, "click", handleClick, { once: true, capture: true }), listen(button1, "click", handleClick, { once: true, capture: true }),
listen(button2, "click", handleClick, true), listen(button2, "click", handleClick, true),
listen(button3, "click", stop_immediate_propagation(handleClick)),
listen(button3, "click", handleTouchstart),
listen(div1, "touchstart", handleTouchstart, { passive: true }) listen(div1, "touchstart", handleTouchstart, { passive: true })
]; ];

@ -13,4 +13,9 @@
<button on:click|stopPropagation|preventDefault={handleClick}>click me</button> <button on:click|stopPropagation|preventDefault={handleClick}>click me</button>
<button on:click|once|capture={handleClick}>or me</button> <button on:click|once|capture={handleClick}>or me</button>
<button on:click|capture={handleClick}>or me!</button> <button on:click|capture={handleClick}>or me!</button>
<button
on:click|stopImmediatePropagation={handleClick}
on:click={handleTouchstart}>
or me!
</button>
</div> </div>

@ -0,0 +1,17 @@
export default {
async test({ assert, component, target, window }) {
const button = target.querySelector('button');
const event = new window.MouseEvent('click');
await button.dispatchEvent(event);
assert.deepEqual(component.logs, ['click_1', 'click_2']);
component.click_2 = () => component.logs.push('22');
await button.dispatchEvent(event);
assert.deepEqual(component.logs, ['click_1', 'click_2', 'click_1', '22']);
component.click_1 = () => component.logs.push('11');
await button.dispatchEvent(event);
assert.deepEqual(component.logs, ['click_1', 'click_2', 'click_1', '22', '11', '22']);
}
};

@ -0,0 +1,22 @@
<script>
export let logs = [];
export let click_1 = () => {
logs.push('click_1');
}
export let click_2 = () => {
logs.push('click_2');
}
export let click_3 = () => {
logs.push('click_3');
}
</script>
<button
on:click={click_1}
on:click|stopImmediatePropagation={click_2}
on:click={click_3}>
click me
</button>

@ -1,5 +1,5 @@
[{ [{
"message": "Valid event modifiers are preventDefault, stopPropagation, capture, once, passive, nonpassive, self or trusted", "message": "Valid event modifiers are preventDefault, stopPropagation, stopImmediatePropagation, capture, once, passive, nonpassive, self or trusted",
"code": "invalid-event-modifier", "code": "invalid-event-modifier",
"start": { "start": {
"line": 1, "line": 1,

Loading…
Cancel
Save