diff --git a/packages/client/src/components/app/Repeater.svelte b/packages/client/src/components/app/Repeater.svelte
index 39e11d12cf..16e1a5363e 100644
--- a/packages/client/src/components/app/Repeater.svelte
+++ b/packages/client/src/components/app/Repeater.svelte
@@ -10,7 +10,7 @@
export let vAlign
export let gap
- const { Provider } = getContext("sdk")
+ const { Provider, ContextScopes } = getContext("sdk")
const component = getContext("component")
$: rows = dataProvider?.rows ?? []
@@ -22,7 +22,7 @@
{:else if rows.length > 0}
{#each rows as row, index}
-
+
{/each}
diff --git a/packages/client/src/components/context/Provider.svelte b/packages/client/src/components/context/Provider.svelte
index b3e9d1e9fc..45ba4deed4 100644
--- a/packages/client/src/components/context/Provider.svelte
+++ b/packages/client/src/components/context/Provider.svelte
@@ -3,15 +3,23 @@
import { dataSourceStore, createContextStore } from "stores"
import { ActionTypes } from "constants"
import { generate } from "shortid"
+ import { ContextScopes } from "constants"
export let data
export let actions
export let key
+ export let scope = ContextScopes.Global
- const context = getContext("context")
+ let context = getContext("context")
const component = getContext("component")
const providerKey = key || $component.id
+ // Create a new layer of context if we are only locally scoped
+ if (scope === ContextScopes.Local) {
+ context = createContextStore(context)
+ setContext("context", context)
+ }
+
// Generate a permanent unique ID for this component and use it to register
// any datasource actions
const instanceId = generate()
@@ -26,7 +34,7 @@
const provideData = newData => {
const dataKey = JSON.stringify(newData)
if (dataKey !== lastDataKey) {
- context.actions.provideData(providerKey, newData)
+ context.actions.provideData(providerKey, newData, scope)
lastDataKey = dataKey
}
}
@@ -36,7 +44,7 @@
if (actionsKey !== lastActionsKey) {
lastActionsKey = actionsKey
newActions?.forEach(({ type, callback, metadata }) => {
- context.actions.provideAction(providerKey, type, callback)
+ context.actions.provideAction(providerKey, type, callback, scope)
// Register any "refresh datasource" actions with a singleton store
// so we can easily refresh data at all levels for any datasource
diff --git a/packages/client/src/constants.js b/packages/client/src/constants.js
index 37a45fbe5d..dfa7985599 100644
--- a/packages/client/src/constants.js
+++ b/packages/client/src/constants.js
@@ -32,5 +32,10 @@ export const ActionTypes = {
ScrollTo: "ScrollTo",
}
+export const ContextScopes = {
+ Local: "local",
+ Global: "global",
+}
+
export const DNDPlaceholderID = "dnd-placeholder"
export const ScreenslotType = "screenslot"
diff --git a/packages/client/src/sdk.js b/packages/client/src/sdk.js
index 237334ca57..6ea1d1b46d 100644
--- a/packages/client/src/sdk.js
+++ b/packages/client/src/sdk.js
@@ -20,7 +20,7 @@ import { getAction } from "utils/getAction"
import Provider from "components/context/Provider.svelte"
import Block from "components/Block.svelte"
import BlockComponent from "components/BlockComponent.svelte"
-import { ActionTypes } from "./constants"
+import { ActionTypes, ContextScopes } from "./constants"
import { fetchDatasourceSchema } from "./utils/schema.js"
import { getAPIKey } from "./utils/api.js"
@@ -44,6 +44,7 @@ export default {
getAction,
fetchDatasourceSchema,
Provider,
+ ContextScopes,
ActionTypes,
getAPIKey,
Block,
diff --git a/packages/client/src/stores/context.js b/packages/client/src/stores/context.js
index 0363fbf7a6..e54c773591 100644
--- a/packages/client/src/stores/context.js
+++ b/packages/client/src/stores/context.js
@@ -1,30 +1,79 @@
-import { writable } from "svelte/store"
+import { writable, derived } from "svelte/store"
+import { ContextScopes } from "constants"
-export const createContextStore = () => {
+export const createContextStore = parentContext => {
const context = writable({})
let observers = []
- // Adds a data context layer to the tree
- const provideData = (providerId, data) => {
+ // Derive the total context state at this point in the tree
+ const contexts = parentContext ? [parentContext, context] : [context]
+ const totalContext = derived(contexts, $contexts => {
+ return $contexts.reduce((total, context) => ({ ...total, ...context }), {})
+ })
+
+ // Subscribe to updates in the parent context, so that we can proxy on any
+ // change messages to our own subscribers
+ if (parentContext) {
+ parentContext.actions.observeChanges(key => {
+ broadcastChange(key)
+ })
+ }
+
+ // Provide some data in context
+ const provideData = (providerId, data, scope = ContextScopes.Global) => {
if (!providerId || data === undefined) {
return
}
- context.update(state => {
- state[providerId] = data
- return state
- })
- broadcastChange(providerId)
+
+ // Proxy message up the chain if we have a parent and are providing global
+ // context
+ if (scope === ContextScopes.Global && parentContext) {
+ parentContext.actions.provideData(providerId, data, scope)
+ }
+
+ // Otherwise this is either the context root, or we're providing a local
+ // context override, so we need to update the local context instead
+ else {
+ context.update(state => {
+ state[providerId] = data
+ return state
+ })
+ broadcastChange(providerId)
+ }
}
- // Adds an action context layer to the tree
- const provideAction = (providerId, actionType, callback) => {
+ // Provides some action in context
+ const provideAction = (
+ providerId,
+ actionType,
+ callback,
+ scope = ContextScopes.Global
+ ) => {
if (!providerId || !actionType) {
return
}
- context.update(state => {
- state[`${providerId}_${actionType}`] = callback
- return state
- })
+
+ // Proxy message up the chain if we have a parent and are providing global
+ // context
+ if (scope === ContextScopes.Global && parentContext) {
+ parentContext.actions.provideAction(
+ providerId,
+ actionType,
+ callback,
+ scope
+ )
+ }
+
+ // Otherwise this is either the context root, or we're providing a local
+ // context override, so we need to update the local context instead
+ else {
+ const key = `${providerId}_${actionType}`
+ context.update(state => {
+ state[key] = callback
+ return state
+ })
+ broadcastChange(key)
+ }
}
const observeChanges = callback => {
@@ -39,7 +88,7 @@ export const createContextStore = () => {
}
return {
- subscribe: context.subscribe,
+ subscribe: totalContext.subscribe,
actions: {
provideData,
provideAction,