diff --git a/index.mjs b/index.mjs index 1ba5890537..768b53f626 100644 --- a/index.mjs +++ b/index.mjs @@ -3,6 +3,8 @@ export { onDestroy, beforeUpdate, afterUpdate, + setContext, + getContext, nextTick, createEventDispatcher } from './internal'; diff --git a/src/internal/Component.js b/src/internal/Component.js index 223e00687f..8a87654eb7 100644 --- a/src/internal/Component.js +++ b/src/internal/Component.js @@ -54,7 +54,7 @@ function make_dirty(component, key) { } export function init(component, options, instance, create_fragment, not_equal) { - const previous_component = current_component; + const parent_component = current_component; set_current_component(component); const props = options.props || {}; @@ -64,7 +64,6 @@ export function init(component, options, instance, create_fragment, not_equal) { ctx: null, // state - set: noop, update: noop, not_equal, bound: blankObject(), @@ -74,6 +73,7 @@ export function init(component, options, instance, create_fragment, not_equal) { on_destroy: [], before_render: [], after_render: [], + context: new Map(parent_component ? parent_component.$$.context : []), // everything else callbacks: blankObject(), @@ -115,7 +115,7 @@ export function init(component, options, instance, create_fragment, not_equal) { flush(); } - set_current_component(previous_component); + set_current_component(parent_component); } export let SvelteElement; diff --git a/src/internal/lifecycle.js b/src/internal/lifecycle.js index e413f4c8f4..02fc2161e1 100644 --- a/src/internal/lifecycle.js +++ b/src/internal/lifecycle.js @@ -4,20 +4,25 @@ export function set_current_component(component) { current_component = component; } +function get_current_component() { + if (!current_component) throw new Error(`Function called outside component initialization`); + return current_component; +} + export function beforeUpdate(fn) { - current_component.$$.before_render.push(fn); + get_current_component().$$.before_render.push(fn); } export function onMount(fn) { - current_component.$$.on_mount.push(fn); + get_current_component().$$.on_mount.push(fn); } export function afterUpdate(fn) { - current_component.$$.after_render.push(fn); + get_current_component().$$.after_render.push(fn); } export function onDestroy(fn) { - current_component.$$.on_destroy.push(fn); + get_current_component().$$.on_destroy.push(fn); } export function createEventDispatcher() { @@ -37,6 +42,14 @@ export function createEventDispatcher() { }; } +export function setContext(key, context) { + get_current_component().$$.context.set(key, context); +} + +export function getContext(key) { + return get_current_component().$$.context.get(key); +} + // TODO figure out if we still want to support // shorthand events, or if we want to implement // a real bubbling mechanism diff --git a/src/internal/scheduler.js b/src/internal/scheduler.js index 0626db2a97..4d9f9ad388 100644 --- a/src/internal/scheduler.js +++ b/src/internal/scheduler.js @@ -1,4 +1,5 @@ import { run_all } from './utils.js'; +import { set_current_component } from './lifecycle.js'; export let dirty_components = []; export const intros = { enabled: false }; @@ -34,7 +35,9 @@ export function flush() { // first, call beforeUpdate functions // and update components while (dirty_components.length) { - update(dirty_components.shift().$$); + const component = dirty_components.shift(); + set_current_component(component); + update(component.$$); } while (binding_callbacks.length) binding_callbacks.shift()(); diff --git a/src/internal/ssr.js b/src/internal/ssr.js index 170f841ec6..762c01a3c5 100644 --- a/src/internal/ssr.js +++ b/src/internal/ssr.js @@ -1,4 +1,4 @@ -import { set_current_component } from './lifecycle.js'; +import { set_current_component, current_component } from './lifecycle.js'; import { run_all, blankObject } from './utils.js'; export const invalidAttributeNameCharacter = /[\s'">\/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u; @@ -68,6 +68,8 @@ export function debug(file, line, column, values) { export function create_ssr_component($$render) { return { render: (props = {}, options = {}) => { + const parent_component = current_component; + // TODO do we need on_ready, since on_mount, // before_render and after_render don't run? const $$ = { @@ -75,6 +77,7 @@ export function create_ssr_component($$render) { on_destroy: [], before_render: [], after_render: [], + context: new Map(parent_component ? parent_component.$$.context : []), callbacks: blankObject() }; diff --git a/test/runtime/samples/context-api/Tab.html b/test/runtime/samples/context-api/Tab.html index 720d5da9de..2f9793aed8 100644 --- a/test/runtime/samples/context-api/Tab.html +++ b/test/runtime/samples/context-api/Tab.html @@ -12,6 +12,6 @@ }); - \ No newline at end of file diff --git a/test/runtime/samples/context-api/TabPanel.html b/test/runtime/samples/context-api/TabPanel.html index 7802ffac26..e15dfca021 100644 --- a/test/runtime/samples/context-api/TabPanel.html +++ b/test/runtime/samples/context-api/TabPanel.html @@ -11,6 +11,6 @@ }); -{#if $selected === panel} +{#if $selectedPanel === panel} {/if} \ No newline at end of file diff --git a/test/runtime/samples/context-api/Tabs.html b/test/runtime/samples/context-api/Tabs.html index 8bbe89a017..6692bb31e9 100644 --- a/test/runtime/samples/context-api/Tabs.html +++ b/test/runtime/samples/context-api/Tabs.html @@ -10,36 +10,40 @@ const tabs = []; const panels = []; - const selected = writable(null); + const selectedTab = writable(null); + const selectedPanel = writable(null); setContext(TABS, { registerTab: tab => { tabs.push(tab); + selectedTab.update(current => current || tab); }, unregisterTab: tab => { const i = tabs.indexOf(tab); tabs.splice(i, 1); + selectedTab.update(current => current === tab ? (tabs[i] || tabs[tabs.length - 1]) : current); }, registerPanel: panel => { panels.push(panel); - - // if this is the first panel, select it - selected.update(current => current || panel); + selectedPanel.update(current => current || panel); }, unregisterPanel: panel => { const i = panels.indexOf(panel); panels.splice(i, 1); + selectedPanel.update(current => current === panel ? (panels[i] || panels[panels.length - 1]) : current); }, selectTab: tab => { const i = tabs.indexOf(tab); - selected.set(panels[i]); + selectedTab.set(tab); + selectedPanel.set(panels[i]); }, - selected + selectedTab, + selectedPanel }); diff --git a/test/runtime/samples/context-api/_config.js b/test/runtime/samples/context-api/_config.js index 0a8c2f1780..1bc475a156 100644 --- a/test/runtime/samples/context-api/_config.js +++ b/test/runtime/samples/context-api/_config.js @@ -19,7 +19,7 @@ export default { assert.htmlEqual(target.innerHTML, `
- +
@@ -32,7 +32,7 @@ export default { assert.htmlEqual(target.innerHTML, `
- +
@@ -48,9 +48,9 @@ export default { assert.htmlEqual(target.innerHTML, `
- + - +

Medium panel

@@ -62,7 +62,7 @@ export default { assert.htmlEqual(target.innerHTML, `
- +