From 5ed6666fa6dc254cc15a7a0e89313efea12f7f8e Mon Sep 17 00:00:00 2001 From: filipot Date: Tue, 18 Aug 2020 23:38:27 +0200 Subject: [PATCH 1/4] Added nonpassive as an event modifier linting linting --- src/compiler/compile/nodes/Element.ts | 3 ++- .../wrappers/Element/EventHandler.ts | 24 ++++++++++++++----- .../event-modifiers-invalid/errors.json | 2 +- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 6636c6b87b4..79bf4e14b8d 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -80,7 +80,8 @@ const valid_modifiers = new Set([ 'capture', 'once', 'passive', - 'self' + 'nonpassive', + 'self', ]); const passive_events = new Set([ diff --git a/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts b/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts index 157e186ea6e..fe02087eb78 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts @@ -45,12 +45,24 @@ export default class EventHandlerWrapper { const args = []; - const opts = ['passive', 'once', 'capture'].filter(mod => this.node.modifiers.has(mod)); - if (opts.length) { - args.push((opts.length === 1 && opts[0] === 'capture') - ? TRUE - : x`{ ${opts.map(opt => p`${opt}: true`)} }`); - } else if (block.renderer.options.dev) { + const opts = ['nonpassive', 'passive', 'once', 'capture'].filter(mod => this.node.modifiers.has(mod)); + if (opts.length) { + let opts_as_string; + + if (opts[0] === 'nonpassive') { + opts.shift(); + + if (!opts.includes('passive')) { + opts.push('passive'); + } + opts_as_string = opts.map(opt => p`${opt}: ${opt === 'passive' ? false : true}`); + } + else { + opts_as_string = opts.map(opt => p`${opt}: true`); + } + args.push((opts.length === 1 && opts[0] === 'capture') ? TRUE : x`{ ${opts_as_string} }`); + } + else if (block.renderer.options.dev) { args.push(FALSE); } diff --git a/test/validator/samples/event-modifiers-invalid/errors.json b/test/validator/samples/event-modifiers-invalid/errors.json index 8be2ca73483..59a142d632c 100644 --- a/test/validator/samples/event-modifiers-invalid/errors.json +++ b/test/validator/samples/event-modifiers-invalid/errors.json @@ -1,5 +1,5 @@ [{ - "message": "Valid event modifiers are preventDefault, stopPropagation, capture, once, passive or self", + "message": "Valid event modifiers are preventDefault, stopPropagation, capture, once, passive, nonpassive or self", "code": "invalid-event-modifier", "start": { "line": 1, From a3be0c93615be2d0d4496f4ac55c5ae9444a7928 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Wed, 23 Sep 2020 08:54:28 +0800 Subject: [PATCH 2/4] add validation and test case --- src/compiler/compile/nodes/Element.ts | 11 ++++-- .../wrappers/Element/EventHandler.ts | 28 ++++++--------- test/js/samples/event-modifiers/expected.js | 36 +++++++++++-------- test/js/samples/event-modifiers/input.svelte | 1 + .../errors.json | 15 ++++++++ .../input.svelte | 3 ++ 6 files changed, 61 insertions(+), 33 deletions(-) create mode 100644 test/validator/samples/event-modifiers-invalid-nonpassive/errors.json create mode 100644 test/validator/samples/event-modifiers-invalid-nonpassive/input.svelte diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 79bf4e14b8d..991f1023397 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -81,7 +81,7 @@ const valid_modifiers = new Set([ 'once', 'passive', 'nonpassive', - 'self', + 'self' ]); const passive_events = new Set([ @@ -771,6 +771,13 @@ export default class Element extends Node { }); } + if (handler.modifiers.has('passive') && handler.modifiers.has('nonpassive')) { + component.error(handler, { + code: 'invalid-event-modifier', + message: `The 'passive' and 'nonpassive' modifiers cannot be used together` + }); + } + handler.modifiers.forEach(modifier => { if (!valid_modifiers.has(modifier)) { component.error(handler, { @@ -805,7 +812,7 @@ export default class Element extends Node { } }); - if (passive_events.has(handler.name) && handler.can_make_passive && !handler.modifiers.has('preventDefault')) { + if (passive_events.has(handler.name) && handler.can_make_passive && !handler.modifiers.has('preventDefault') && !handler.modifiers.has('nonpassive')) { // touch/wheel events should be passive by default handler.modifiers.add('passive'); } diff --git a/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts b/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts index fe02087eb78..2fa2e9291a8 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts @@ -46,23 +46,17 @@ export default class EventHandlerWrapper { const args = []; const opts = ['nonpassive', 'passive', 'once', 'capture'].filter(mod => this.node.modifiers.has(mod)); - if (opts.length) { - let opts_as_string; - - if (opts[0] === 'nonpassive') { - opts.shift(); - - if (!opts.includes('passive')) { - opts.push('passive'); - } - opts_as_string = opts.map(opt => p`${opt}: ${opt === 'passive' ? false : true}`); - } - else { - opts_as_string = opts.map(opt => p`${opt}: true`); - } - args.push((opts.length === 1 && opts[0] === 'capture') ? TRUE : x`{ ${opts_as_string} }`); - } - else if (block.renderer.options.dev) { + if (opts.length) { + if (opts.length === 1 && opts[0] === 'capture') { + args.push(TRUE); + } else { + args.push(x`{ ${ opts.map(opt => + opt === 'nonpassive' + ? p`passive: false` + : p`${opt}: true` + ) } }`); + } + } else if (block.renderer.options.dev) { args.push(FALSE); } diff --git a/test/js/samples/event-modifiers/expected.js b/test/js/samples/event-modifiers/expected.js index 6aa3c161f92..3901753661d 100644 --- a/test/js/samples/event-modifiers/expected.js +++ b/test/js/samples/event-modifiers/expected.js @@ -16,41 +16,49 @@ import { } from "svelte/internal"; function create_fragment(ctx) { - let div; - let button0; + let div1; + let div0; let t1; - let button1; + let button0; let t3; + let button1; + let t5; let button2; let mounted; let dispose; return { c() { - div = element("div"); + div1 = element("div"); + div0 = element("div"); + div0.textContent = "touch me"; + t1 = space(); button0 = element("button"); button0.textContent = "click me"; - t1 = space(); + t3 = space(); button1 = element("button"); button1.textContent = "or me"; - t3 = space(); + t5 = space(); button2 = element("button"); button2.textContent = "or me!"; }, m(target, anchor) { - insert(target, div, anchor); - append(div, button0); - append(div, t1); - append(div, button1); - append(div, t3); - append(div, button2); + insert(target, div1, anchor); + append(div1, div0); + append(div1, t1); + append(div1, button0); + append(div1, t3); + append(div1, button1); + append(div1, t5); + append(div1, button2); if (!mounted) { dispose = [ + listen(div0, "touchstart", handleTouchstart, { passive: false }), listen(button0, "click", stop_propagation(prevent_default(handleClick))), listen(button1, "click", handleClick, { once: true, capture: true }), listen(button2, "click", handleClick, true), - listen(div, "touchstart", handleTouchstart, { passive: true }) + listen(div1, "touchstart", handleTouchstart, { passive: true }) ]; mounted = true; @@ -60,7 +68,7 @@ function create_fragment(ctx) { i: noop, o: noop, d(detaching) { - if (detaching) detach(div); + if (detaching) detach(div1); mounted = false; run_all(dispose); } diff --git a/test/js/samples/event-modifiers/input.svelte b/test/js/samples/event-modifiers/input.svelte index 225134f5983..c72d58dabb7 100644 --- a/test/js/samples/event-modifiers/input.svelte +++ b/test/js/samples/event-modifiers/input.svelte @@ -9,6 +9,7 @@
+
touch me
diff --git a/test/validator/samples/event-modifiers-invalid-nonpassive/errors.json b/test/validator/samples/event-modifiers-invalid-nonpassive/errors.json new file mode 100644 index 00000000000..a7e5a2a76ce --- /dev/null +++ b/test/validator/samples/event-modifiers-invalid-nonpassive/errors.json @@ -0,0 +1,15 @@ +[{ + "message": "The 'passive' and 'nonpassive' modifiers cannot be used together", + "code": "invalid-event-modifier", + "start": { + "line": 1, + "column": 5, + "character": 5 + }, + "end": { + "line": 1, + "column": 51, + "character": 51 + }, + "pos": 5 +}] diff --git a/test/validator/samples/event-modifiers-invalid-nonpassive/input.svelte b/test/validator/samples/event-modifiers-invalid-nonpassive/input.svelte new file mode 100644 index 00000000000..3557aa4b1dc --- /dev/null +++ b/test/validator/samples/event-modifiers-invalid-nonpassive/input.svelte @@ -0,0 +1,3 @@ +
+ oops +
\ No newline at end of file From e6f259eca94c164b38b6791a4380cc2bb1d4c001 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Wed, 23 Sep 2020 09:16:55 +0800 Subject: [PATCH 3/4] add docs --- site/content/docs/02-template-syntax.md | 1 + site/content/tutorial/05-events/03-event-modifiers/text.md | 1 + 2 files changed, 2 insertions(+) diff --git a/site/content/docs/02-template-syntax.md b/site/content/docs/02-template-syntax.md index e93449728bf..f7e1ae35173 100644 --- a/site/content/docs/02-template-syntax.md +++ b/site/content/docs/02-template-syntax.md @@ -471,6 +471,7 @@ The following modifiers are available: * `preventDefault` — calls `event.preventDefault()` before running the handler * `stopPropagation` — calls `event.stopPropagation()`, preventing the event reaching the next element * `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` * `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase * `once` — remove the handler after the first time it runs * `self` — only trigger handler if event.target is the element itself diff --git a/site/content/tutorial/05-events/03-event-modifiers/text.md b/site/content/tutorial/05-events/03-event-modifiers/text.md index 41154cafcd4..2b2d6e6b315 100644 --- a/site/content/tutorial/05-events/03-event-modifiers/text.md +++ b/site/content/tutorial/05-events/03-event-modifiers/text.md @@ -21,6 +21,7 @@ The full list of modifiers: * `preventDefault` — calls `event.preventDefault()` before running the handler. Useful for client-side form handling, for example. * `stopPropagation` — calls `event.stopPropagation()`, preventing the event reaching the next element * `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` * `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase ([MDN docs](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture)) * `once` — remove the handler after the first time it runs * `self` — only trigger handler if event.target is the element itself From b110579abb5665f0ac1cc58bf3c2bcbe190889c6 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Thu, 24 Sep 2020 15:48:56 -0400 Subject: [PATCH 4/4] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c514152672f..0938d627e50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +* Add `|nonpassive` event modifier, explicitly passing `passive: false` ([#2068](https://github.com/sveltejs/svelte/issues/2068)) * Fix keyed `{#each}` not reacting to key changing ([#5444](https://github.com/sveltejs/svelte/issues/5444)) * Fix destructuring into store values ([#5449](https://github.com/sveltejs/svelte/issues/5449)) * Fix erroneous `missing-declaration` warning with `use:obj.method` ([#5451](https://github.com/sveltejs/svelte/issues/5451))