mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] qweb, blockdom, components: t-on with modifiers
supported modifiers: capture, prevent, stop, self.
This commit is contained in:
committed by
Aaron Bohy
parent
2ae0149adb
commit
4cceb239dd
@@ -16,12 +16,13 @@ export const config = {
|
||||
// this is the main event handler. Every event handler registered with blockdom
|
||||
// will go through this function, giving it the data registered in the block
|
||||
// and the event
|
||||
mainEventHandler: (data: any, ev: Event) => {
|
||||
mainEventHandler: (data: any, ev: Event, currentTarget?: EventTarget | null): boolean => {
|
||||
if (typeof data === "function") {
|
||||
data(ev);
|
||||
} else if (Array.isArray(data)) {
|
||||
data = filterOutModifiersFromData(data).data;
|
||||
data[0](data[1], ev);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
+19
-10
@@ -9,27 +9,32 @@ interface EventHandlerCreator {
|
||||
|
||||
export function createEventHandler(rawEvent: string): EventHandlerCreator {
|
||||
const eventName = rawEvent.split(".")[0];
|
||||
const capture = rawEvent.includes(".capture");
|
||||
if (rawEvent.includes(".synthetic")) {
|
||||
return createSyntheticHandler(eventName);
|
||||
return createSyntheticHandler(eventName, capture);
|
||||
} else {
|
||||
return createElementHandler(eventName);
|
||||
return createElementHandler(eventName, capture);
|
||||
}
|
||||
}
|
||||
|
||||
// Native listener
|
||||
function createElementHandler(evName: string): EventHandlerCreator {
|
||||
function createElementHandler(evName: string, capture: boolean = false): EventHandlerCreator {
|
||||
let eventKey = `__event__${evName}`;
|
||||
if (capture) {
|
||||
eventKey = `${eventKey}_capture`;
|
||||
}
|
||||
|
||||
function listener(ev: Event) {
|
||||
const currentTarget = ev.currentTarget;
|
||||
if (!currentTarget || !document.contains(currentTarget as HTMLElement)) return;
|
||||
const data = (currentTarget as any)[eventKey];
|
||||
if (!data) return;
|
||||
config.mainEventHandler(data, ev);
|
||||
config.mainEventHandler(data, ev, currentTarget);
|
||||
}
|
||||
|
||||
function setup(this: HTMLElement, data: any) {
|
||||
(this as any)[eventKey] = data;
|
||||
this.addEventListener(evName, listener);
|
||||
this.addEventListener(evName, listener, { capture });
|
||||
}
|
||||
|
||||
function update(this: HTMLElement, data: any) {
|
||||
@@ -41,9 +46,12 @@ function createElementHandler(evName: string): EventHandlerCreator {
|
||||
|
||||
// Synthetic handler: a form of event delegation that allows placing only one
|
||||
// listener per event type.
|
||||
function createSyntheticHandler(evName: string): EventHandlerCreator {
|
||||
function createSyntheticHandler(evName: string, capture: boolean = false): EventHandlerCreator {
|
||||
let eventKey = `__event__synthetic_${evName}`;
|
||||
setupSyntheticEvent(evName, eventKey);
|
||||
if (capture) {
|
||||
eventKey = `${eventKey}_capture`;
|
||||
}
|
||||
setupSyntheticEvent(evName, eventKey, capture);
|
||||
function setup(this: HTMLElement, data: any) {
|
||||
(this as any)[eventKey] = data;
|
||||
}
|
||||
@@ -55,7 +63,8 @@ function nativeToSyntheticEvent(eventKey: string, event: Event) {
|
||||
while (dom !== null) {
|
||||
const data = (dom as any)[eventKey];
|
||||
if (data) {
|
||||
config.mainEventHandler(data, event);
|
||||
const stopped = config.mainEventHandler(data, event, dom);
|
||||
if (stopped) return;
|
||||
}
|
||||
dom = (dom as any).parentNode;
|
||||
}
|
||||
@@ -63,10 +72,10 @@ function nativeToSyntheticEvent(eventKey: string, event: Event) {
|
||||
|
||||
const CONFIGURED_SYNTHETIC_EVENTS: { [event: string]: boolean } = {};
|
||||
|
||||
function setupSyntheticEvent(evName: string, eventKey: string) {
|
||||
function setupSyntheticEvent(evName: string, eventKey: string, capture: boolean = false) {
|
||||
if (CONFIGURED_SYNTHETIC_EVENTS[eventKey]) {
|
||||
return;
|
||||
}
|
||||
document.addEventListener(evName, (event) => nativeToSyntheticEvent(eventKey, event));
|
||||
document.addEventListener(evName, (event) => nativeToSyntheticEvent(eventKey, event), { capture });
|
||||
CONFIGURED_SYNTHETIC_EVENTS[eventKey] = true;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
import { filterOutModifiersFromData } from "../blockdom/config";
|
||||
|
||||
export function mainEventHandler(data: any, ev: Event) {
|
||||
if (typeof data === "function") {
|
||||
data(ev);
|
||||
} else {
|
||||
data = filterOutModifiersFromData(data).data;
|
||||
const ctx = data[0];
|
||||
export const mainEventHandler = (data: any, ev: Event, currentTarget?: EventTarget|null) => {
|
||||
const { data: _data, modifiers } = filterOutModifiersFromData(data);
|
||||
data = _data;
|
||||
let stopped = false;
|
||||
if (modifiers.length) {
|
||||
let selfMode = false;
|
||||
const isSelf = ev.target === currentTarget;
|
||||
for (const mod of modifiers) {
|
||||
switch (mod) {
|
||||
case "self": selfMode = true; if (isSelf) { continue; } else { return stopped; };
|
||||
case "prevent": if ((selfMode && isSelf) || (!selfMode)) ev.preventDefault(); continue;
|
||||
case "stop": if ((selfMode && isSelf) || (!selfMode)) ev.stopPropagation() ; stopped = true; continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeof data[0] === "function") {
|
||||
data[0](ev);
|
||||
} else if (data[0].__owl__) {
|
||||
const method = data[1];
|
||||
const args = data[2] || [];
|
||||
ctx.__owl__.component[method](...args, ev);
|
||||
data[0].__owl__.component[method](...args, ev);
|
||||
}
|
||||
return stopped;
|
||||
}
|
||||
|
||||
@@ -468,12 +468,17 @@ export class QWebCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
generateHandlerCode(handler: string, event: string = ""): string {
|
||||
generateHandlerCode(rawEvent: string, handler: string): string {
|
||||
let args: string = "";
|
||||
const name: string = handler.replace(/\(.*\)/, function (_args) {
|
||||
args = _args.slice(1, -1);
|
||||
return "";
|
||||
});
|
||||
const modifiers = rawEvent.split(".").slice(1).map(m => `"${m}"`);
|
||||
let modifiersCode = "";
|
||||
if (modifiers.length) {
|
||||
modifiersCode = `${modifiers.join(",")}, `;
|
||||
}
|
||||
const isMethodCall = name.match(FNAMEREGEXP);
|
||||
if (isMethodCall) {
|
||||
let handlerFn: string;
|
||||
@@ -484,15 +489,11 @@ export class QWebCompiler {
|
||||
} else {
|
||||
handlerFn = `'${name}'`;
|
||||
}
|
||||
return `[${event ? `\`${event}\`` + ", " : ""}ctx, ${handlerFn!}]`;
|
||||
return `[${modifiersCode}ctx, ${handlerFn!}]`;
|
||||
} else {
|
||||
let code = this.captureExpression(handler);
|
||||
code = `{const res = (() => { return ${code} })(); if (typeof res === 'function') { res(e) }}`;
|
||||
let handlerFn = `(e) => ${code}`;
|
||||
if (event) {
|
||||
handlerFn = `[\`${event}\`, ${handlerFn}]`;
|
||||
}
|
||||
return handlerFn;
|
||||
return `[${modifiersCode}(e) => ${code}]`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,7 +529,7 @@ export class QWebCompiler {
|
||||
|
||||
// event handlers
|
||||
for (let ev in ast.on) {
|
||||
const name = this.generateHandlerCode(ast.on[ev]);
|
||||
const name = this.generateHandlerCode(ev, ast.on[ev]);
|
||||
const idx = block!.insertData(name);
|
||||
attrs[`block-handler-${idx}`] = ev;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user