implement event modifiers

pull/1839/head
Rich Harris 7 years ago
parent e394493f71
commit 75bbd6b878

@ -17,6 +17,7 @@ export default class Attribute extends Node {
isSpread: boolean;
isTrue: boolean;
isDynamic: boolean;
isStatic: boolean;
isSynthetic: boolean;
shouldCache: boolean;
expression?: Expression;
@ -37,12 +38,14 @@ export default class Attribute extends Node {
this.chunks = null;
this.isDynamic = true; // TODO not necessarily
this.isStatic = false;
this.shouldCache = false; // TODO does this mean anything here?
}
else {
this.name = info.name;
this.isTrue = info.value === true;
this.isStatic = true;
this.isSynthetic = info.synthetic;
this.dependencies = new Set();
@ -52,6 +55,8 @@ export default class Attribute extends Node {
: info.value.map(node => {
if (node.type === 'Text') return node;
this.isStatic = false;
const expression = new Expression(component, this, scope, node.expression);
addToSet(this.dependencies, expression.dynamic_dependencies);

@ -337,7 +337,7 @@ export default class Element extends Node {
}
if (name === 'slot') {
if (attribute.isDynamic) {
if (!attribute.isStatic) {
component.error(attribute, {
code: `invalid-slot-attribute`,
message: `slot attribute cannot have a dynamic value`
@ -425,7 +425,7 @@ export default class Element extends Node {
if (!attribute) return null;
if (attribute.isDynamic) {
if (!attribute.isStatic) {
component.error(attribute, {
code: `invalid-type`,
message: `'type' attribute cannot be dynamic if input uses two-way binding`
@ -464,7 +464,7 @@ export default class Element extends Node {
(attribute: Attribute) => attribute.name === 'multiple'
);
if (attribute && attribute.isDynamic) {
if (attribute && !attribute.isStatic) {
component.error(attribute, {
code: `dynamic-multiple-attribute`,
message: `'multiple' attribute cannot be dynamic if select uses two-way binding`

@ -7,14 +7,12 @@ export default function addEventHandlers(
handlers: EventHandler[]
) {
handlers.forEach(handler => {
const modifiers = [];
if (handler.modifiers.has('preventDefault')) modifiers.push('event.preventDefault();');
if (handler.modifiers.has('stopPropagation')) modifiers.push('event.stopPropagation();');
let snippet = handler.render();
if (handler.modifiers.has('preventDefault')) snippet = `@preventDefault(${snippet})`;
if (handler.modifiers.has('stopPropagation')) snippet = `@stopPropagation(${snippet})`;
const opts = ['passive', 'once', 'capture'].filter(mod => handler.modifiers.has(mod));
const snippet = handler.render();
if (opts.length) {
const optString = (opts.length === 1 && opts[0] === 'capture')
? 'true'

@ -78,6 +78,20 @@ export function addListener(node, event, handler, options) {
return () => node.removeEventListener(event, handler, options);
}
export function preventDefault(fn) {
return function(event) {
event.preventDefault();
return fn.call(this, event);
};
}
export function stopPropagation(fn) {
return function(event) {
event.stopPropagation();
return fn.call(this, event);
};
}
export function setAttribute(node, attribute, value) {
if (value == null) node.removeAttribute(attribute);
else node.setAttribute(attribute, value);

@ -1,15 +1,9 @@
/* generated by Svelte vX.Y.Z-alpha1 */
import { SvelteComponent as SvelteComponent_1, addListener, append, createElement, createText, detachNode, init, insert, noop, run, run_all, safe_not_equal } from "svelte/internal.js";
import { SvelteComponent as SvelteComponent_1, addListener, append, createElement, createText, detachNode, init, insert, noop, preventDefault, run, run_all, safe_not_equal, stopPropagation } from "svelte/internal.js";
function create_fragment(component, ctx) {
var div, button0, text1, button1, text3, button2, current, dispose;
function click_handler(event) {
event.stopPropagation();
event.preventDefault();
handleClick(event);
}
return {
c() {
div = createElement("div");
@ -22,7 +16,7 @@ function create_fragment(component, ctx) {
button2 = createElement("button");
button2.textContent = "or me!";
dispose = [
addListener(button0, click_handler),
addListener(button0, "click", stopPropagation(preventDefault(handleClick))),
addListener(button1, "click", handleClick, { once: true, capture: true }),
addListener(button2, "click", handleClick, true),
addListener(div, "touchstart", handleTouchstart, { passive: true })

@ -0,0 +1,16 @@
export default {
html: `
<button>0</button>
`,
async test({ assert, component, target, window }) {
const button = target.querySelector('button');
const event = new window.MouseEvent('click');
await button.dispatchEvent(event);
assert.equal(component.count, 1);
await button.dispatchEvent(event);
assert.equal(component.count, 1);
}
};

@ -0,0 +1,5 @@
<script>
export let count = 0;
</script>
<button on:click|once="{() => count += 1}">{count}</button>

@ -0,0 +1,16 @@
export default {
html: `
<button>click me</button>
`,
async test({ assert, component, target, window }) {
const button = target.querySelector('button');
const event = new window.MouseEvent('click', {
cancelable: true
});
await button.dispatchEvent(event);
assert.ok(component.default_was_prevented);
}
};

@ -0,0 +1,9 @@
<script>
export let default_was_prevented;
function handle_click(event) {
default_was_prevented = event.defaultPrevented;
}
</script>
<button on:click|preventDefault={handle_click}>click me</button>

@ -0,0 +1,19 @@
export default {
html: `
<div>
<button>click me</button>
</div>
`,
async test({ assert, component, target, window }) {
const button = target.querySelector('button');
const event = new window.MouseEvent('click', {
bubbles: true
});
await button.dispatchEvent(event);
assert.ok(component.inner_clicked);
assert.ok(!component.outer_clicked);
}
};

@ -0,0 +1,16 @@
<script>
export let inner_clicked;
export let outer_clicked;
function handle_inner_click(event) {
inner_clicked = true;
}
function handle_outer_click(event) {
outer_clicked = true;
}
</script>
<div on:click={handle_outer_click}>
<button on:click|stopPropagation={handle_inner_click}>click me</button>
</div>
Loading…
Cancel
Save