Skip to content

Commit 51af8c2

Browse files
committed
event propagation shorthand for elements (#638)
1 parent 5c4905a commit 51af8c2

File tree

7 files changed

+66
-29
lines changed

7 files changed

+66
-29
lines changed

src/generators/dom/visitors/Element/EventHandler.ts

+26-21
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,33 @@ export default function visitEventHandler(
1616
const isCustomEvent = generator.events.has(name);
1717
const shouldHoist = !isCustomEvent && state.inEachBlock;
1818

19-
generator.addSourcemapLocations(attribute.expression);
20-
21-
const flattened = flattenReference(attribute.expression.callee);
22-
if (flattened.name !== 'event' && flattened.name !== 'this') {
23-
// allow event.stopPropagation(), this.select() etc
24-
// TODO verify that it's a valid callee (i.e. built-in or declared method)
25-
generator.code.prependRight(
26-
attribute.expression.start,
27-
`${block.alias('component')}.`
28-
);
29-
if (shouldHoist) state.usesComponent = true; // this feels a bit hacky but it works!
30-
}
31-
3219
const context = shouldHoist ? null : state.parentNode;
3320
const usedContexts: string[] = [];
34-
attribute.expression.arguments.forEach((arg: Node) => {
35-
const { contexts } = block.contextualise(arg, context, true);
3621

37-
contexts.forEach(context => {
38-
if (!~usedContexts.indexOf(context)) usedContexts.push(context);
39-
if (!~state.allUsedContexts.indexOf(context))
40-
state.allUsedContexts.push(context);
22+
if (attribute.expression) {
23+
generator.addSourcemapLocations(attribute.expression);
24+
25+
const flattened = flattenReference(attribute.expression.callee);
26+
if (flattened.name !== 'event' && flattened.name !== 'this') {
27+
// allow event.stopPropagation(), this.select() etc
28+
// TODO verify that it's a valid callee (i.e. built-in or declared method)
29+
generator.code.prependRight(
30+
attribute.expression.start,
31+
`${block.alias('component')}.`
32+
);
33+
if (shouldHoist) state.usesComponent = true; // this feels a bit hacky but it works!
34+
}
35+
36+
attribute.expression.arguments.forEach((arg: Node) => {
37+
const { contexts } = block.contextualise(arg, context, true);
38+
39+
contexts.forEach(context => {
40+
if (!~usedContexts.indexOf(context)) usedContexts.push(context);
41+
if (!~state.allUsedContexts.indexOf(context))
42+
state.allUsedContexts.push(context);
43+
});
4144
});
42-
});
45+
}
4346

4447
const _this = context || 'this';
4548
const declarations = usedContexts.map(name => {
@@ -66,7 +69,9 @@ export default function visitEventHandler(
6669
${state.usesComponent &&
6770
`var ${block.alias('component')} = ${_this}._svelte.component;`}
6871
${declarations}
69-
[✂${attribute.expression.start}-${attribute.expression.end}✂];
72+
${attribute.expression ?
73+
`[✂${attribute.expression.start}-${attribute.expression.end}✂];` :
74+
`${block.alias('component')}.fire('${attribute.name}', event);`}
7075
`;
7176

7277
if (isCustomEvent) {

src/parse/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ export class Parser {
111111
if (required) {
112112
this.error(`Expected ${str}`);
113113
}
114+
115+
return false;
114116
}
115117

116118
match(str: string) {

src/parse/read/directives.ts

+11-6
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,21 @@ function readExpression(parser: Parser, start: number, quoteMark) {
4343
export function readEventHandlerDirective(
4444
parser: Parser,
4545
start: number,
46-
name: string
46+
name: string,
47+
hasValue: boolean
4748
) {
48-
const quoteMark = parser.eat(`'`) ? `'` : parser.eat(`"`) ? `"` : null;
49+
let expression;
4950

50-
const expressionStart = parser.index;
51+
if (hasValue) {
52+
const quoteMark = parser.eat(`'`) ? `'` : parser.eat(`"`) ? `"` : null;
5153

52-
const expression = readExpression(parser, expressionStart, quoteMark);
54+
const expressionStart = parser.index;
5355

54-
if (expression.type !== 'CallExpression') {
55-
parser.error(`Expected call expression`, expressionStart);
56+
expression = readExpression(parser, expressionStart, quoteMark);
57+
58+
if (expression.type !== 'CallExpression') {
59+
parser.error(`Expected call expression`, expressionStart);
60+
}
5661
}
5762

5863
return {

src/parse/state/tag.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,7 @@ function readAttribute(parser: Parser, uniqueNames) {
256256
parser.allowWhitespace();
257257

258258
if (/^on:/.test(name)) {
259-
parser.eat('=', true);
260-
return readEventHandlerDirective(parser, start, name.slice(3));
259+
return readEventHandlerDirective(parser, start, name.slice(3), parser.eat('='));
261260
}
262261

263262
if (/^bind:/.test(name)) {

src/validate/html/validateEventHandler.ts

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export default function validateEventHandlerCallee(
99
validator: Validator,
1010
attribute: Node
1111
) {
12+
if (!attribute.expression) return;
13+
1214
const { callee, start, type } = attribute.expression;
1315

1416
if (type !== 'CallExpression') {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export default {
2+
html: `
3+
<button>click me</button>
4+
`,
5+
6+
test (assert, component, target, window) {
7+
const button = target.querySelector('button');
8+
const event = new window.MouseEvent('click');
9+
10+
button.dispatchEvent(event);
11+
assert.ok(component.clicked);
12+
}
13+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<button on:click>click me</button>
2+
3+
<script>
4+
export default {
5+
oncreate () {
6+
this.on('click', () => {
7+
this.clicked = true;
8+
});
9+
}
10+
};
11+
</script>

0 commit comments

Comments
 (0)