get events sorta working

pull/1864/head
Rich Harris 7 years ago
parent 80ea0572e2
commit 56eeead1b3

@ -35,130 +35,13 @@ export default class EventHandler extends Node {
this.dependencies = new Set();
if (info.expression) {
this.validateExpression(info.expression);
this.callee = flattenReference(info.expression.callee);
this.insertionPoint = info.expression.start;
this.usesComponent = !validCalleeObjects.has(this.callee.name);
this.usesContext = false;
this.usesEventObject = this.callee.name === 'event';
this.args = info.expression.arguments.map(param => {
const expression = new Expression(component, this, scope, param);
addToSet(this.dependencies, expression.dependencies);
if (expression.usesContext) this.usesContext = true;
if (expression.usesEvent) this.usesEventObject = true;
return expression;
});
this.snippet = `[✂${info.expression.start}-${info.expression.end}✂];`;
this.expression = new Expression(component, parent, scope, info.expression);
this.snippet = this.expression.snippet;
} else {
this.callee = null;
this.insertionPoint = null;
this.args = null;
this.usesComponent = true;
this.usesContext = false;
this.usesEventObject = true;
this.snippet = null; // TODO handle shorthand events here?
}
this.isCustomEvent = component.events.has(this.name);
this.shouldHoist = !this.isCustomEvent && parent.hasAncestor('EachBlock');
}
render(component, block, context, hoisted) { // TODO hoist more event handlers
if (this.insertionPoint === null) return; // TODO handle shorthand events here?
if (!validCalleeObjects.has(this.callee.name)) {
const component_name = hoisted ? `component` : block.alias(`component`);
// allow event.stopPropagation(), this.select() etc
// TODO verify that it's a valid callee (i.e. built-in or declared method)
if (this.callee.name[0] === '$' && !component.methods.has(this.callee.name)) {
component.code.overwrite(
this.insertionPoint,
this.insertionPoint + 1,
`${component_name}.store.`
);
} else {
component.code.prependRight(
this.insertionPoint,
`${component_name}.`
);
}
}
if (this.isCustomEvent) {
this.args.forEach(arg => {
arg.overwriteThis(context);
});
if (this.callee && this.callee.name === 'this') {
const node = this.callee.nodes[0];
component.code.overwrite(node.start, node.end, context, {
storeName: true,
contentOnly: true
});
}
}
}
validateExpression(expression) {
const { callee, type } = expression;
if (type !== 'CallExpression') {
this.component.error(expression, {
code: `invalid-event-handler`,
message: `Expected a call expression`
});
}
const { component } = this;
const { name } = flattenReference(callee);
if (validCalleeObjects.has(name) || name === 'options') return;
if (name === 'refs') {
this.component.refCallees.push(callee);
return;
}
if (
(callee.type === 'Identifier' && validBuiltins.has(name)) ||
this.component.methods.has(name)
) {
return;
}
if (name[0] === '$') {
// assume it's a store method
return;
}
const validCallees = ['this.*', 'refs.*', 'event.*', 'options.*', 'console.*'].concat(
Array.from(validBuiltins),
Array.from(this.component.methods.keys())
);
let message = `'${component.source.slice(callee.start, callee.end)}' is an invalid callee ` ;
if (name === 'store') {
message += `(did you mean '$${component.source.slice(callee.start + 6, callee.end)}(...)'?)`;
} else {
message += `(should be one of ${list(validCallees)})`;
if (callee.type === 'Identifier' && component.helpers.has(callee.name)) {
message += `. '${callee.name}' exists on 'helpers', did you put it in the wrong place?`;
}
}
component.warn(expression, {
code: `invalid-callee`,
message
});
}
}

@ -84,7 +84,6 @@ export default class Expression {
let { map, scope: currentScope } = createScopes(info);
const isEventHandler = parent.type === 'EventHandler';
const expression = this;
const isSynthetic = parent.isSynthetic;
@ -108,11 +107,6 @@ export default class Expression {
if (isReference(node, parent)) {
const { name, nodes } = flattenReference(node);
if (name === 'event' && isEventHandler) {
expression.usesEvent = true;
return;
}
if (currentScope.has(name)) return;
if (component.helpers.has(name)) {

@ -608,11 +608,6 @@ export default class ElementWrapper extends Wrapper {
this.node.handlers.forEach(handler => {
const isCustomEvent = component.events.has(handler.name);
if (handler.callee) {
// TODO move handler render method into a wrapper
handler.render(this.renderer.component, block, this.var, handler.shouldHoist);
}
const target = handler.shouldHoist ? 'this' : this.var;
// get a name for the event handler that is globally unique
@ -656,7 +651,7 @@ export default class ElementWrapper extends Wrapper {
const handlerFunction = deindent`
function ${handlerName}(event) {
${modifiers}
${handlerBody}
(${handlerBody})(event);
}
`;

@ -47,12 +47,10 @@ export default class WindowWrapper extends Wrapper {
let usesState = handler.dependencies.size > 0;
handler.render(component, block, 'window', false); // TODO hoist?
const handlerName = block.getUniqueName(`onwindow${handler.name}`);
const handlerBody = deindent`
${usesState && `var ctx = #component.get();`}
${handler.snippet};
${handler.snippet}
`;
if (isCustomEvent) {
@ -61,7 +59,7 @@ export default class WindowWrapper extends Wrapper {
block.builders.hydrate.addBlock(deindent`
${handlerName} = %events-${handler.name}.call(#component, window, function(event) {
${handlerBody}
(${handlerBody})(event);
});
`);
@ -71,7 +69,7 @@ export default class WindowWrapper extends Wrapper {
} else {
block.builders.init.addBlock(deindent`
function ${handlerName}(event) {
${handlerBody}
(${handlerBody})(event);
}
window.addEventListener("${handler.name}", ${handlerName});
`);

@ -413,6 +413,7 @@ function readAttribute(parser: Parser, uniqueNames: Set<string>) {
function get_directive_type(name) {
if (name === 'use') return 'Action';
if (name === 'on') return 'EventHandler';
throw new Error(`TODO directive ${name}`);
}

@ -3,25 +3,25 @@ export default {
<button>action</button>
`,
test (assert, component, target, window) {
async test (assert, component, target, window) {
const button = target.querySelector('button');
const eventEnter = new window.MouseEvent('mouseenter');
const eventLeave = new window.MouseEvent('mouseleave');
const enter = new window.MouseEvent('mouseenter');
const leave = new window.MouseEvent('mouseleave');
const ctrlPress = new window.KeyboardEvent('keydown', { ctrlKey: true });
button.dispatchEvent(eventEnter);
await button.dispatchEvent(enter);
assert.htmlEqual(target.innerHTML, `
<button>action</button>
<div class="tooltip">Perform an Action</div>
`);
window.dispatchEvent(ctrlPress);
await window.dispatchEvent(ctrlPress);
assert.htmlEqual(target.innerHTML, `
<button>action</button>
<div class="tooltip">Perform an augmented Action</div>
`);
button.dispatchEvent(eventLeave);
await button.dispatchEvent(leave);
assert.htmlEqual(target.innerHTML, `
<button>action</button>
`);

@ -2,10 +2,11 @@
export let text = 'Perform an Action';
function checkForCtrl(event) {
console.log(`!!! ${event.ctrlKey}`);
if (event.ctrlKey) {
tooltip = 'Perform an augmented Action';
text = 'Perform an augmented Action';
} else {
tooltip = 'Perform an Action';
text = 'Perform an Action';
}
}

Loading…
Cancel
Save