mirror of https://github.com/sveltejs/svelte
Merge pull request #3836 from tanhauhau/tanhauhau/dynamic-event-handler
feat dynamic event handlerpull/3876/head
commit
60f7f79a70
@ -0,0 +1,69 @@
|
||||
import EventHandler from '../../../nodes/EventHandler';
|
||||
import Wrapper from '../shared/Wrapper';
|
||||
import Block from '../../Block';
|
||||
import { b, x, p } from 'code-red';
|
||||
|
||||
const TRUE = x`true`;
|
||||
const FALSE = x`false`;
|
||||
|
||||
export default class EventHandlerWrapper {
|
||||
node: EventHandler;
|
||||
parent: Wrapper;
|
||||
|
||||
constructor(node: EventHandler, parent: Wrapper) {
|
||||
this.node = node;
|
||||
this.parent = parent;
|
||||
|
||||
if (!node.expression) {
|
||||
this.parent.renderer.component.add_var({
|
||||
name: node.handler_name.name,
|
||||
internal: true,
|
||||
referenced: true,
|
||||
});
|
||||
|
||||
this.parent.renderer.component.partly_hoisted.push(b`
|
||||
function ${node.handler_name.name}(event) {
|
||||
@bubble($$self, event);
|
||||
}
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
get_snippet(block) {
|
||||
const snippet = this.node.expression ? this.node.expression.manipulate(block) : x`#ctx.${this.node.handler_name}`;
|
||||
|
||||
if (this.node.reassigned) {
|
||||
block.maintain_context = true;
|
||||
return x`function () { ${snippet}.apply(this, arguments); }`;
|
||||
}
|
||||
return snippet;
|
||||
}
|
||||
|
||||
render(block: Block, target: string) {
|
||||
let snippet = this.get_snippet(block);
|
||||
|
||||
if (this.node.modifiers.has('preventDefault')) snippet = x`@prevent_default(${snippet})`;
|
||||
if (this.node.modifiers.has('stopPropagation')) snippet = x`@stop_propagation(${snippet})`;
|
||||
if (this.node.modifiers.has('self')) snippet = x`@self(${snippet})`;
|
||||
|
||||
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) {
|
||||
args.push(FALSE);
|
||||
}
|
||||
|
||||
if (block.renderer.options.dev) {
|
||||
args.push(this.node.modifiers.has('stopPropagation') ? TRUE : FALSE);
|
||||
args.push(this.node.modifiers.has('preventDefault') ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
block.event_listeners.push(
|
||||
x`@listen(${target}, "${this.node.name}", ${snippet}, ${args})`
|
||||
);
|
||||
}
|
||||
}
|
@ -1,39 +1,10 @@
|
||||
import Block from '../../Block';
|
||||
import EventHandler from '../../../nodes/EventHandler';
|
||||
import { x, p } from 'code-red';
|
||||
|
||||
const TRUE = x`true`;
|
||||
const FALSE = x`false`;
|
||||
import EventHandler from '../Element/EventHandler';
|
||||
|
||||
export default function add_event_handlers(
|
||||
block: Block,
|
||||
target: string,
|
||||
handlers: EventHandler[]
|
||||
) {
|
||||
handlers.forEach(handler => {
|
||||
let snippet = handler.render(block);
|
||||
if (handler.modifiers.has('preventDefault')) snippet = x`@prevent_default(${snippet})`;
|
||||
if (handler.modifiers.has('stopPropagation')) snippet = x`@stop_propagation(${snippet})`;
|
||||
if (handler.modifiers.has('self')) snippet = x`@self(${snippet})`;
|
||||
|
||||
const args = [];
|
||||
|
||||
const opts = ['passive', 'once', 'capture'].filter(mod => handler.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) {
|
||||
args.push(FALSE);
|
||||
}
|
||||
|
||||
if (block.renderer.options.dev) {
|
||||
args.push(handler.modifiers.has('stopPropagation') ? TRUE : FALSE);
|
||||
args.push(handler.modifiers.has('preventDefault') ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
block.event_listeners.push(
|
||||
x`@listen(${target}, "${handler.name}", ${snippet}, ${args})`
|
||||
);
|
||||
});
|
||||
handlers.forEach(handler => handler.render(block, target));
|
||||
}
|
||||
|
@ -0,0 +1,107 @@
|
||||
import {
|
||||
SvelteComponent,
|
||||
append,
|
||||
detach,
|
||||
element,
|
||||
init,
|
||||
insert,
|
||||
listen,
|
||||
noop,
|
||||
run_all,
|
||||
safe_not_equal,
|
||||
set_data,
|
||||
space,
|
||||
text
|
||||
} from "svelte/internal";
|
||||
|
||||
function create_fragment(ctx) {
|
||||
let p0;
|
||||
let button0;
|
||||
let t1;
|
||||
let button1;
|
||||
let t3;
|
||||
let p1;
|
||||
let t4;
|
||||
let t5;
|
||||
let button2;
|
||||
let dispose;
|
||||
|
||||
return {
|
||||
c() {
|
||||
p0 = element("p");
|
||||
button0 = element("button");
|
||||
button0.textContent = "set handler 1";
|
||||
t1 = space();
|
||||
button1 = element("button");
|
||||
button1.textContent = "set handler 2";
|
||||
t3 = space();
|
||||
p1 = element("p");
|
||||
t4 = text(ctx.number);
|
||||
t5 = space();
|
||||
button2 = element("button");
|
||||
button2.textContent = "click";
|
||||
|
||||
dispose = [
|
||||
listen(button0, "click", ctx.updateHandler1),
|
||||
listen(button1, "click", ctx.updateHandler2),
|
||||
listen(button2, "click", function () {
|
||||
ctx.clickHandler.apply(this, arguments);
|
||||
})
|
||||
];
|
||||
},
|
||||
m(target, anchor) {
|
||||
insert(target, p0, anchor);
|
||||
append(p0, button0);
|
||||
append(p0, t1);
|
||||
append(p0, button1);
|
||||
insert(target, t3, anchor);
|
||||
insert(target, p1, anchor);
|
||||
append(p1, t4);
|
||||
insert(target, t5, anchor);
|
||||
insert(target, button2, anchor);
|
||||
},
|
||||
p(changed, new_ctx) {
|
||||
ctx = new_ctx;
|
||||
if (changed.number) set_data(t4, ctx.number);
|
||||
},
|
||||
i: noop,
|
||||
o: noop,
|
||||
d(detaching) {
|
||||
if (detaching) detach(p0);
|
||||
if (detaching) detach(t3);
|
||||
if (detaching) detach(p1);
|
||||
if (detaching) detach(t5);
|
||||
if (detaching) detach(button2);
|
||||
run_all(dispose);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function instance($$self, $$props, $$invalidate) {
|
||||
let clickHandler;
|
||||
let number = 0;
|
||||
|
||||
function updateHandler1() {
|
||||
$$invalidate("clickHandler", clickHandler = () => $$invalidate("number", number = 1));
|
||||
}
|
||||
|
||||
function updateHandler2() {
|
||||
$$invalidate("clickHandler", clickHandler = () => $$invalidate("number", number = 2));
|
||||
}
|
||||
|
||||
return {
|
||||
clickHandler,
|
||||
number,
|
||||
updateHandler1,
|
||||
updateHandler2
|
||||
};
|
||||
}
|
||||
|
||||
class Component extends SvelteComponent {
|
||||
constructor(options) {
|
||||
super();
|
||||
init(this, options, instance, create_fragment, safe_not_equal, {});
|
||||
}
|
||||
}
|
||||
|
||||
export default Component;
|
@ -0,0 +1,23 @@
|
||||
<script>
|
||||
let clickHandler;
|
||||
let number = 0;
|
||||
|
||||
function updateHandler1(){
|
||||
clickHandler = () => number = 1;
|
||||
}
|
||||
|
||||
function updateHandler2(){
|
||||
clickHandler = () => number = 2;
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<p>
|
||||
<button on:click={updateHandler1}>set handler 1</button>
|
||||
<button on:click={updateHandler2}>set handler 2</button>
|
||||
</p>
|
||||
|
||||
<p>{ number }</p>
|
||||
|
||||
<button on:click={clickHandler}>click</button>
|
@ -0,0 +1 @@
|
||||
<button on:click><slot></slot></button>
|
@ -0,0 +1,18 @@
|
||||
export default {
|
||||
html: `
|
||||
<button>update handler</button>
|
||||
<button>0</button>
|
||||
`,
|
||||
|
||||
async test({ assert, component, target, window }) {
|
||||
const [updateButton, button] = target.querySelectorAll('button');
|
||||
const event = new window.MouseEvent('click');
|
||||
|
||||
await button.dispatchEvent(event);
|
||||
assert.equal(component.count, 1);
|
||||
|
||||
await updateButton.dispatchEvent(event);
|
||||
await button.dispatchEvent(event);
|
||||
assert.equal(component.count, 11);
|
||||
}
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import Button from './Button.svelte';
|
||||
export let count = 0;
|
||||
let clickHandler = () => count += 1;
|
||||
function updateHandler(){
|
||||
clickHandler = () => count += 10;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={updateHandler}>update handler</button>
|
||||
<Button on:click={clickHandler}>{count}</Button>
|
@ -0,0 +1 @@
|
||||
<button on:click><slot></slot></button>
|
@ -0,0 +1,18 @@
|
||||
export default {
|
||||
html: `
|
||||
<button>update handler</button>
|
||||
<button>0</button>
|
||||
`,
|
||||
|
||||
async test({ assert, component, target, window }) {
|
||||
const [updateButton, button] = target.querySelectorAll('button');
|
||||
const event = new window.MouseEvent('click');
|
||||
|
||||
await updateButton.dispatchEvent(event);
|
||||
await button.dispatchEvent(event);
|
||||
assert.equal(component.count, 10);
|
||||
|
||||
await button.dispatchEvent(event);
|
||||
assert.equal(component.count, 10);
|
||||
}
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import Button from './Button.svelte';
|
||||
export let count = 0;
|
||||
let clickHandler = () => count += 1;
|
||||
function updateHandler(){
|
||||
clickHandler = () => count += 10;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={updateHandler}>update handler</button>
|
||||
<Button on:click|once={clickHandler}>{count}</Button>
|
@ -0,0 +1,50 @@
|
||||
export default {
|
||||
html: `
|
||||
<p>
|
||||
<button>set handler 1</button>
|
||||
<button>set handler 2</button>
|
||||
</p>
|
||||
<p>0</p>
|
||||
<button>click</button>
|
||||
`,
|
||||
|
||||
async test({ assert, component, target, window }) {
|
||||
const [updateButton1, updateButton2, button] = target.querySelectorAll(
|
||||
'button'
|
||||
);
|
||||
|
||||
const event = new window.MouseEvent('click');
|
||||
|
||||
await button.dispatchEvent(event);
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<p>
|
||||
<button>set handler 1</button>
|
||||
<button>set handler 2</button>
|
||||
</p>
|
||||
<p>0</p>
|
||||
<button>click</button>
|
||||
`);
|
||||
|
||||
await updateButton1.dispatchEvent(event);
|
||||
await button.dispatchEvent(event);
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<p>
|
||||
<button>set handler 1</button>
|
||||
<button>set handler 2</button>
|
||||
</p>
|
||||
<p>1</p>
|
||||
<button>click</button>
|
||||
`);
|
||||
|
||||
await updateButton2.dispatchEvent(event);
|
||||
await button.dispatchEvent(event);
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<p>
|
||||
<button>set handler 1</button>
|
||||
<button>set handler 2</button>
|
||||
</p>
|
||||
<p>2</p>
|
||||
<button>click</button>
|
||||
`);
|
||||
},
|
||||
};
|
@ -0,0 +1,23 @@
|
||||
<script>
|
||||
let clickHandler;
|
||||
let number = 0;
|
||||
|
||||
function updateHandler1(){
|
||||
clickHandler = () => number = 1;
|
||||
}
|
||||
|
||||
function updateHandler2(){
|
||||
clickHandler = () => number = 2;
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<p>
|
||||
<button on:click={updateHandler1}>set handler 1</button>
|
||||
<button on:click={updateHandler2}>set handler 2</button>
|
||||
</p>
|
||||
|
||||
<p>{ number }</p>
|
||||
|
||||
<button on:click={clickHandler}>click</button>
|
Loading…
Reference in new issue