diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts index 2c9c4af3d0..b0db9040ea 100644 --- a/src/runtime/internal/Component.ts +++ b/src/runtime/internal/Component.ts @@ -1,9 +1,9 @@ import { add_render_callback, flush, flush_render_callbacks, schedule_update, dirty_components } from './scheduler'; -import { current_component, set_current_component, start_callback, stop_callback } from './lifecycle'; +import { add_callback, current_component, set_current_component } from './lifecycle'; import { blank_object, is_empty, is_function, run, run_all, noop } from './utils'; import { children, detach, start_hydrating, end_hydrating } from './dom'; import { transition_in } from './transitions'; -import { Callback, T$$ } from './types'; +import { T$$ } from './types'; export function bind(component, name, callback) { const index = component.$$.props[name]; @@ -181,22 +181,7 @@ if (typeof HTMLElement === 'function') { } $on(type: string, callback: EventListener, options?: boolean | AddEventListenerOptions | EventListenerOptions) { - if (!is_function(callback)) { - return noop; - } - const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = [])); - const c: Callback = {f:callback, o:options}; - callbacks.push(c); - - start_callback(this as any, type, c); - - return () => { - const index = callbacks.indexOf(c); - if (index !== -1) { - callbacks.splice(index, 1); - stop_callback(this as any, type, c); - } - }; + return add_callback(this, type, callback, options); } $set($$props) { @@ -222,20 +207,7 @@ export class SvelteComponent { } $on(type: string, callback: EventListener, options?: boolean | AddEventListenerOptions | EventListenerOptions) { - if (!is_function(callback)) { - return noop; - } - const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = [])); - const c: Callback = {f:callback, o:options}; - callbacks.push(c); - - start_callback(this as any, type, c); - - return () => { - const index = callbacks.indexOf(c); - if (index !== -1) callbacks.splice(index, 1); - stop_callback(this as any, type, c); - }; + return add_callback(this, type, callback, options); } $set($$props) { diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index b8d2dbd083..83aef3e0e4 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -1,7 +1,7 @@ import { SvelteComponent } from './Component'; import { custom_event, wrap_handler } from './dom'; import { Bubble, Callback, CallbackFactory } from './types'; -import { noop } from './utils'; +import { is_function, noop } from './utils'; export let current_component; @@ -159,7 +159,15 @@ function start_bubbles(comp : SvelteComponent, bubble: Bubble) { } } -export function start_callback(comp : SvelteComponent, type: string, callback: Callback) { +export function restart_all_callback(comp : SvelteComponent) { + for (const type of Object.keys(comp.$$.callbacks)) { + for (const callback of comp.$$.callbacks[type]) { + start_callback(comp, type, callback); + } + } +} + +function start_callback(comp : SvelteComponent, type: string, callback: Callback) { for (const bubbles of [ comp.$$.bubbles[type], comp.$$.bubbles['*'] ]) { if (bubbles) { for(const bubble of bubbles) { @@ -169,7 +177,28 @@ export function start_callback(comp : SvelteComponent, type: string, callback: C } } -export function stop_callback(comp : SvelteComponent, type: string, callback: Callback) { +export function add_callback(comp : SvelteComponent, type: string, f: EventListener, o?: boolean | AddEventListenerOptions | EventListenerOptions) { + if (!is_function(f)) { + return noop; + } + + const callbacks = (comp.$$.callbacks[type] || (comp.$$.callbacks[type] = [])); + const callback: Callback = {f, o}; + + if (o && (o as any)?.once === true) { + callback.f = function () { + const r = f.apply(this, arguments); + remove_callback(comp, type, callback); + return r; + }; + } + + callbacks.push(callback); + start_callback(comp, type, callback); + return () => remove_callback(comp, type, callback); +} + +function stop_callback(comp : SvelteComponent, type: string, callback: Callback) { for (const bubbles of [ comp.$$.bubbles[type], comp.$$.bubbles['*'] ]) { if (bubbles) { for (const bubble of bubbles) { @@ -183,6 +212,17 @@ export function stop_callback(comp : SvelteComponent, type: string, callback: Ca } } +function remove_callback(comp : SvelteComponent, type: string, callback: Callback) { + const callbacks = comp.$$.callbacks[type]; + if (callbacks) { + const index = callbacks.indexOf(callback); + if (index !== -1) { + callbacks.splice(index, 1); + stop_callback(comp, type, callback); + } + } +} + function add_bubble(comp: SvelteComponent, type: string, f: CallbackFactory): Function { const bubble : Bubble = {f, r: new Map()}; const bubbles = (comp.$$.bubbles[type] || (comp.$$.bubbles[type] = []));