From ed8cc0093e8b4fa1a222ddae0bdcd39802726c86 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Dec 2021 14:59:46 +0800 Subject: [PATCH 01/12] [feat] dispatch cancelable custom events --- src/runtime/internal/dev.ts | 2 +- src/runtime/internal/dom.ts | 9 +++++++-- src/runtime/internal/lifecycle.ts | 6 +++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/runtime/internal/dev.ts b/src/runtime/internal/dev.ts index 76d68086c84..2f3072713d4 100644 --- a/src/runtime/internal/dev.ts +++ b/src/runtime/internal/dev.ts @@ -2,7 +2,7 @@ import { custom_event, append, append_hydration, insert, insert_hydration, detac import { SvelteComponent } from './Component'; export function dispatch_dev(type: string, detail?: T) { - document.dispatchEvent(custom_event(type, { version: '__VERSION__', ...detail }, true)); + document.dispatchEvent(custom_event(type, { version: '__VERSION__', ...detail }, { bubbles: true })); } export function append_dev(target: Node, node: Node) { diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index eb3389e3f86..8367ab619d7 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -630,9 +630,14 @@ export function toggle_class(element, name, toggle) { element.classList[toggle ? 'add' : 'remove'](name); } -export function custom_event(type: string, detail?: T, bubbles: boolean = false) { +export interface CustomEventOpts { + bubbles?: boolean; + cancelable?: boolean; +} + +export function custom_event(type: string, detail?: T, opts?: CustomEventOpts) { const e: CustomEvent = document.createEvent('CustomEvent'); - e.initCustomEvent(type, bubbles, false, detail); + e.initCustomEvent(type, opts.bubbles, opts.cancelable, detail); return e; } diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index bb3df3d2952..610557ed183 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -1,4 +1,4 @@ -import { custom_event } from './dom'; +import { custom_event, CustomEventOpts } from './dom'; export let current_component; @@ -32,13 +32,13 @@ export function createEventDispatcher< >(): >(type: EventKey, detail?: EventMap[EventKey]) => void { const component = get_current_component(); - return (type: string, detail?: any) => { + return (type: string, detail?: any, opts?: CustomEventOpts) => { const callbacks = component.$$.callbacks[type]; if (callbacks) { // TODO are there situations where events could be dispatched // in a server (non-DOM) environment? - const event = custom_event(type, detail); + const event = custom_event(type, detail, opts); callbacks.slice().forEach(fn => { fn.call(component, event); }); From c86bf54222121f0c2e1155622b43be1fdb058f7a Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Dec 2021 15:09:34 +0800 Subject: [PATCH 02/12] fix typo --- src/runtime/internal/dom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index 8367ab619d7..69f5cc07b1a 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -635,7 +635,7 @@ export interface CustomEventOpts { cancelable?: boolean; } -export function custom_event(type: string, detail?: T, opts?: CustomEventOpts) { +export function custom_event(type: string, detail?: T, opts: CustomEventOpts = {}) { const e: CustomEvent = document.createEvent('CustomEvent'); e.initCustomEvent(type, opts.bubbles, opts.cancelable, detail); return e; From e1cdf849dced134fac5b408007e27a730f16c130 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Dec 2021 21:52:46 +0800 Subject: [PATCH 03/12] return boolean for dispatch --- src/runtime/internal/dom.ts | 8 ++------ src/runtime/internal/lifecycle.ts | 9 ++++++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index 69f5cc07b1a..255c071ac45 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -630,14 +630,10 @@ export function toggle_class(element, name, toggle) { element.classList[toggle ? 'add' : 'remove'](name); } -export interface CustomEventOpts { - bubbles?: boolean; - cancelable?: boolean; -} -export function custom_event(type: string, detail?: T, opts: CustomEventOpts = {}) { +export function custom_event(type: string, detail?: T, { bubbles = false, cancelable = false } = {}): CustomEvent { const e: CustomEvent = document.createEvent('CustomEvent'); - e.initCustomEvent(type, opts.bubbles, opts.cancelable, detail); + e.initCustomEvent(type, bubbles, cancelable, detail); return e; } diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index 610557ed183..aac8ebe0b7a 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -1,4 +1,4 @@ -import { custom_event, CustomEventOpts } from './dom'; +import { custom_event } from './dom'; export let current_component; @@ -32,17 +32,20 @@ export function createEventDispatcher< >(): >(type: EventKey, detail?: EventMap[EventKey]) => void { const component = get_current_component(); - return (type: string, detail?: any, opts?: CustomEventOpts) => { + return (type: string, detail?: any, { cancelable = false } = {}): boolean => { const callbacks = component.$$.callbacks[type]; if (callbacks) { // TODO are there situations where events could be dispatched // in a server (non-DOM) environment? - const event = custom_event(type, detail, opts); + const event = custom_event(type, detail, { cancelable }); callbacks.slice().forEach(fn => { fn.call(component, event); }); + return !event.defaultPrevented; } + + return true; }; } From 2d4eac9bf1c6b8161a7df2f808c6ae4ba3d19dab Mon Sep 17 00:00:00 2001 From: bluwy Date: Sun, 30 Jan 2022 21:25:54 +0800 Subject: [PATCH 04/12] add docs --- site/content/docs/03-run-time.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/site/content/docs/03-run-time.md b/site/content/docs/03-run-time.md index 54228f5c2a5..2295722ca71 100644 --- a/site/content/docs/03-run-time.md +++ b/site/content/docs/03-run-time.md @@ -228,7 +228,7 @@ dispatch: ((name: string, detail?: any) => void) = createEventDispatcher(); Creates an event dispatcher that can be used to dispatch [component events](/docs#template-syntax-component-directives-on-component-event). Event dispatchers are functions that can take two arguments: `name` and `detail`. -Component events created with `createEventDispatcher` create a [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture) and are not cancellable with `event.preventDefault()`. The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) property and can contain any type of data. +Component events created with `createEventDispatcher` create a [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture). The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) property and can contain any type of data. ```sv +``` + ### `svelte/store` The `svelte/store` module exports functions for creating [readable](/docs#run-time-svelte-store-readable), [writable](/docs#run-time-svelte-store-writable) and [derived](/docs#run-time-svelte-store-derived) stores. From b0db18fe13204a2f0ac1649f076997ab01cb8b5d Mon Sep 17 00:00:00 2001 From: bluwy Date: Sun, 30 Jan 2022 21:26:44 +0800 Subject: [PATCH 05/12] add changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bdb6f3964f..991b82f7a12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Svelte changelog +## Unreleased + +* Add a third parameter to the returned function of `createEventDispatcher` that allows passing an object of `{ cancelable: true }` to create a cancelable custom event. The returned function will also return a boolean depending on whether the event is cancelled. + ## 3.46.3 * Ignore whitespace in `{#each}` blocks when containing elements with `animate:` ([#5477](https://github.com/sveltejs/svelte/pull/5477)) From 6333f79580aaa9bafedb450a07ab9e40832b6adb Mon Sep 17 00:00:00 2001 From: bluwy Date: Sun, 30 Jan 2022 21:34:10 +0800 Subject: [PATCH 06/12] update types --- src/runtime/internal/lifecycle.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index aac8ebe0b7a..6f326b2b9e1 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -27,9 +27,17 @@ export function onDestroy(fn: () => any) { get_current_component().$$.on_destroy.push(fn); } -export function createEventDispatcher< - EventMap extends {} = any ->(): >(type: EventKey, detail?: EventMap[EventKey]) => void { +interface DispatchOptions { + cancelable?: boolean; +} + +export function createEventDispatcher(): < + EventKey extends Extract +>( + type: EventKey, + detail?: EventMap[EventKey], + options?: DispatchOptions +) => void { const component = get_current_component(); return (type: string, detail?: any, { cancelable = false } = {}): boolean => { From 97b6f2af53c6f3803e60a32b2eb91a4c1f3020fe Mon Sep 17 00:00:00 2001 From: bluwy Date: Sun, 30 Jan 2022 21:36:44 +0800 Subject: [PATCH 07/12] fix typing again --- site/content/docs/03-run-time.md | 2 +- src/runtime/internal/lifecycle.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/site/content/docs/03-run-time.md b/site/content/docs/03-run-time.md index 2295722ca71..a68c6a33ae6 100644 --- a/site/content/docs/03-run-time.md +++ b/site/content/docs/03-run-time.md @@ -221,7 +221,7 @@ Retrieves the whole context map that belongs to the closest parent component. Mu #### `createEventDispatcher` ```js -dispatch: ((name: string, detail?: any) => void) = createEventDispatcher(); +dispatch: ((name: string, detail?: any, options?: DispatchOptions) => boolean) = createEventDispatcher(); ``` --- diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index 6f326b2b9e1..cd726598a84 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -37,7 +37,7 @@ export function createEventDispatcher(): < type: EventKey, detail?: EventMap[EventKey], options?: DispatchOptions -) => void { +) => boolean { const component = get_current_component(); return (type: string, detail?: any, { cancelable = false } = {}): boolean => { From fe6564f6ddac221940cbfdcf5a0d18897006951c Mon Sep 17 00:00:00 2001 From: bluwy Date: Sun, 30 Jan 2022 23:05:06 +0800 Subject: [PATCH 08/12] export DispatchOptions --- src/runtime/index.ts | 1 + src/runtime/internal/lifecycle.ts | 2 +- src/runtime/ssr.ts | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/runtime/index.ts b/src/runtime/index.ts index 8e12f9f0eeb..96b2b84b995 100644 --- a/src/runtime/index.ts +++ b/src/runtime/index.ts @@ -11,6 +11,7 @@ export { hasContext, tick, createEventDispatcher, + DispatchOptions, SvelteComponentDev as SvelteComponent, SvelteComponentTyped } from 'svelte/internal'; diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index cd726598a84..5a9dc3d4d27 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -27,7 +27,7 @@ export function onDestroy(fn: () => any) { get_current_component().$$.on_destroy.push(fn); } -interface DispatchOptions { +export interface DispatchOptions { cancelable?: boolean; } diff --git a/src/runtime/ssr.ts b/src/runtime/ssr.ts index cbac1f3ef85..9685d986aab 100644 --- a/src/runtime/ssr.ts +++ b/src/runtime/ssr.ts @@ -6,6 +6,7 @@ export { hasContext, tick, createEventDispatcher, + DispatchOptions, SvelteComponent, SvelteComponentTyped } from './index'; From c0b2f217b7599c4f8dc66c35fa9da176b6a34f40 Mon Sep 17 00:00:00 2001 From: bluwy Date: Sun, 30 Jan 2022 23:05:12 +0800 Subject: [PATCH 09/12] update docs --- site/content/docs/03-run-time.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/docs/03-run-time.md b/site/content/docs/03-run-time.md index a68c6a33ae6..0340e2a0eb2 100644 --- a/site/content/docs/03-run-time.md +++ b/site/content/docs/03-run-time.md @@ -256,7 +256,7 @@ Events dispatched from child components can be listened to in their parent. Any --- -Events can also be cancelable by passing a third parameter to the dispatch function. Event listeners can then call `event.preventDefault()` to control the dispatcher's return value. +Events can be cancelable by passing a third parameter to the dispatch function. The function returns `false` if the event is cancelled with `event.preventDefault()`, otherwise it returns `true`. ```sv