Support both global and local state simultaneously
This commit is contained in:
parent
1b47fb2308
commit
49948df9cd
5 changed files with 85 additions and 22 deletions
|
@ -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 @@
|
|||
<Placeholder />
|
||||
{:else if rows.length > 0}
|
||||
{#each rows as row, index}
|
||||
<Provider data={{ ...row, index }}>
|
||||
<Provider data={{ ...row, index }} scope={ContextScopes.Local}>
|
||||
<slot />
|
||||
</Provider>
|
||||
{/each}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue