Merge pull request #12931 from Budibase/nested-context-fix
Remove proxying of context changes up the chain
This commit is contained in:
commit
1b55fcca97
|
@ -7,6 +7,9 @@ import {
|
|||
findHBSBlocks,
|
||||
} from "@budibase/string-templates"
|
||||
import { capitalise } from "helpers"
|
||||
import { Constants } from "@budibase/frontend-core"
|
||||
|
||||
const { ContextScopes } = Constants
|
||||
|
||||
/**
|
||||
* Recursively searches for a specific component ID
|
||||
|
@ -263,11 +266,59 @@ export const getComponentName = component => {
|
|||
if (component == null) {
|
||||
return ""
|
||||
}
|
||||
|
||||
const components = get(store)?.components || {}
|
||||
const componentDefinition = components[component._component] || {}
|
||||
const name =
|
||||
componentDefinition.friendlyName || componentDefinition.name || ""
|
||||
|
||||
return name
|
||||
return componentDefinition.friendlyName || componentDefinition.name || ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Recurses through the component tree and builds a tree of contexts provided
|
||||
* by components.
|
||||
*/
|
||||
export const buildContextTree = (
|
||||
rootComponent,
|
||||
tree = { root: [] },
|
||||
currentBranch = "root"
|
||||
) => {
|
||||
// Sanity check
|
||||
if (!rootComponent) {
|
||||
return tree
|
||||
}
|
||||
|
||||
// Process this component's contexts
|
||||
const def = store.actions.components.getDefinition(rootComponent._component)
|
||||
if (def?.context) {
|
||||
tree[currentBranch].push(rootComponent._id)
|
||||
const contexts = Array.isArray(def.context) ? def.context : [def.context]
|
||||
|
||||
// If we provide local context, start a new branch for our children
|
||||
if (contexts.some(context => context.scope === ContextScopes.Local)) {
|
||||
currentBranch = rootComponent._id
|
||||
tree[rootComponent._id] = []
|
||||
}
|
||||
}
|
||||
|
||||
// Process children
|
||||
if (rootComponent._children) {
|
||||
rootComponent._children.forEach(child => {
|
||||
buildContextTree(child, tree, currentBranch)
|
||||
})
|
||||
}
|
||||
|
||||
return tree
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a lookup map of which context branch all components in a component
|
||||
* tree are inside.
|
||||
*/
|
||||
export const buildContextTreeLookupMap = rootComponent => {
|
||||
const tree = buildContextTree(rootComponent)
|
||||
let map = {}
|
||||
Object.entries(tree).forEach(([branch, ids]) => {
|
||||
ids.forEach(id => {
|
||||
map[id] = branch
|
||||
})
|
||||
})
|
||||
return map
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { cloneDeep } from "lodash/fp"
|
||||
import { get } from "svelte/store"
|
||||
import {
|
||||
buildContextTreeLookupMap,
|
||||
findAllComponents,
|
||||
findAllMatchingComponents,
|
||||
findComponent,
|
||||
|
@ -20,11 +21,13 @@ import {
|
|||
encodeJSBinding,
|
||||
} from "@budibase/string-templates"
|
||||
import { TableNames } from "../constants"
|
||||
import { JSONUtils } from "@budibase/frontend-core"
|
||||
import { JSONUtils, Constants } from "@budibase/frontend-core"
|
||||
import ActionDefinitions from "components/design/settings/controls/ButtonActionEditor/manifest.json"
|
||||
import { environment, licensing } from "stores/portal"
|
||||
import { convertOldFieldFormat } from "components/design/settings/controls/FieldConfiguration/utils"
|
||||
|
||||
const { ContextScopes } = Constants
|
||||
|
||||
// Regex to match all instances of template strings
|
||||
const CAPTURE_VAR_INSIDE_TEMPLATE = /{{([^}]+)}}/g
|
||||
const CAPTURE_VAR_INSIDE_JS = /\$\("([^")]+)"\)/g
|
||||
|
@ -214,13 +217,42 @@ export const getComponentContexts = (
|
|||
return []
|
||||
}
|
||||
let map = {}
|
||||
const componentPath = findComponentPath(asset.props, componentId)
|
||||
const componentPathIds = componentPath.map(component => component._id)
|
||||
const contextTreeLookupMap = buildContextTreeLookupMap(asset.props)
|
||||
|
||||
// Processes all contexts exposed by a component
|
||||
const processContexts = scope => component => {
|
||||
const def = store.actions.components.getDefinition(component._component)
|
||||
// Sanity check
|
||||
const def = store.actions.components.getDefinition(component?._component)
|
||||
if (!def?.context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Filter out global contexts not in the same branch.
|
||||
// Global contexts are only valid if their branch root is an ancestor of
|
||||
// this component.
|
||||
const branch = contextTreeLookupMap[component._id]
|
||||
if (branch !== "root" && !componentPathIds.includes(branch)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Process all contexts provided by this component
|
||||
const contexts = Array.isArray(def.context) ? def.context : [def.context]
|
||||
contexts.forEach(context => {
|
||||
// Ensure type matches
|
||||
if (type && context.type !== type) {
|
||||
return
|
||||
}
|
||||
// Ensure scope matches
|
||||
let contextScope = context.scope || ContextScopes.Global
|
||||
if (contextScope !== scope) {
|
||||
return
|
||||
}
|
||||
// Ensure the context is compatible with the component's current settings
|
||||
if (!isContextCompatibleWithComponent(context, component)) {
|
||||
return
|
||||
}
|
||||
if (!map[component._id]) {
|
||||
map[component._id] = {
|
||||
component,
|
||||
|
@ -228,32 +260,16 @@ export const getComponentContexts = (
|
|||
contexts: [],
|
||||
}
|
||||
}
|
||||
const contexts = Array.isArray(def.context) ? def.context : [def.context]
|
||||
contexts.forEach(context => {
|
||||
// Ensure type matches
|
||||
if (type && context.type !== type) {
|
||||
return
|
||||
}
|
||||
// Ensure scope matches
|
||||
let contextScope = context.scope || "global"
|
||||
if (contextScope !== scope) {
|
||||
return
|
||||
}
|
||||
// Ensure the context is compatible with the component's current settings
|
||||
if (!isContextCompatibleWithComponent(context, component)) {
|
||||
return
|
||||
}
|
||||
map[component._id].contexts.push(context)
|
||||
})
|
||||
}
|
||||
|
||||
// Process all global contexts
|
||||
const allComponents = findAllComponents(asset.props)
|
||||
allComponents.forEach(processContexts("global"))
|
||||
allComponents.forEach(processContexts(ContextScopes.Global))
|
||||
|
||||
// Process all local contexts
|
||||
const localComponents = findComponentPath(asset.props, componentId)
|
||||
localComponents.forEach(processContexts("local"))
|
||||
// Process all local contexts in the immediate tree
|
||||
componentPath.forEach(processContexts(ContextScopes.Local))
|
||||
|
||||
// Exclude self if required
|
||||
if (!options?.includeSelf) {
|
||||
|
|
|
@ -4720,7 +4720,8 @@
|
|||
}
|
||||
],
|
||||
"context": {
|
||||
"type": "schema"
|
||||
"type": "schema",
|
||||
"scope": "local"
|
||||
}
|
||||
},
|
||||
"daterangepicker": {
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
import { getContext } from "svelte"
|
||||
import Placeholder from "./Placeholder.svelte"
|
||||
import Container from "./Container.svelte"
|
||||
import { ContextScopes } from "constants"
|
||||
|
||||
const { Provider, ContextScopes } = getContext("sdk")
|
||||
const component = getContext("component")
|
||||
|
||||
export let dataProvider
|
||||
export let noRowsMessage
|
||||
|
@ -12,9 +14,6 @@
|
|||
export let gap
|
||||
export let scope = ContextScopes.Local
|
||||
|
||||
const { Provider } = getContext("sdk")
|
||||
const component = getContext("component")
|
||||
|
||||
$: rows = dataProvider?.rows ?? []
|
||||
$: loaded = dataProvider?.loaded ?? true
|
||||
</script>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<script>
|
||||
import { getContext, setContext, onDestroy } from "svelte"
|
||||
import { dataSourceStore, createContextStore } from "stores"
|
||||
import { ActionTypes, ContextScopes } from "constants"
|
||||
import { ActionTypes } from "constants"
|
||||
import { generate } from "shortid"
|
||||
|
||||
const { ContextScopes } = getContext("sdk")
|
||||
|
||||
export let data
|
||||
export let actions
|
||||
export let key
|
||||
|
@ -33,7 +35,7 @@
|
|||
const provideData = newData => {
|
||||
const dataKey = JSON.stringify(newData)
|
||||
if (dataKey !== lastDataKey) {
|
||||
context.actions.provideData(providerKey, newData, scope)
|
||||
context.actions.provideData(providerKey, newData)
|
||||
lastDataKey = dataKey
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +45,7 @@
|
|||
if (actionsKey !== lastActionsKey) {
|
||||
lastActionsKey = actionsKey
|
||||
newActions?.forEach(({ type, callback, metadata }) => {
|
||||
context.actions.provideAction(providerKey, type, callback, scope)
|
||||
context.actions.provideAction(providerKey, type, callback)
|
||||
|
||||
// Register any "refresh datasource" actions with a singleton store
|
||||
// so we can easily refresh data at all levels for any datasource
|
||||
|
|
|
@ -12,10 +12,5 @@ export const ActionTypes = {
|
|||
ScrollTo: "ScrollTo",
|
||||
}
|
||||
|
||||
export const ContextScopes = {
|
||||
Local: "local",
|
||||
Global: "global",
|
||||
}
|
||||
|
||||
export const DNDPlaceholderID = "dnd-placeholder"
|
||||
export const ScreenslotType = "screenslot"
|
||||
|
|
|
@ -23,12 +23,12 @@ 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, ContextScopes } from "./constants"
|
||||
import { ActionTypes } from "./constants"
|
||||
import { fetchDatasourceSchema } from "./utils/schema.js"
|
||||
import { getAPIKey } from "./utils/api.js"
|
||||
import { enrichButtonActions } from "./utils/buttonActions.js"
|
||||
import { processStringSync, makePropSafe } from "@budibase/string-templates"
|
||||
import { fetchData, LuceneUtils } from "@budibase/frontend-core"
|
||||
import { fetchData, LuceneUtils, Constants } from "@budibase/frontend-core"
|
||||
|
||||
export default {
|
||||
API,
|
||||
|
@ -57,7 +57,7 @@ export default {
|
|||
fetchDatasourceSchema,
|
||||
fetchData,
|
||||
LuceneUtils,
|
||||
ContextScopes,
|
||||
ContextScopes: Constants.ContextScopes,
|
||||
getAPIKey,
|
||||
enrichButtonActions,
|
||||
processStringSync,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { writable, derived } from "svelte/store"
|
||||
import { ContextScopes } from "constants"
|
||||
|
||||
export const createContextStore = parentContext => {
|
||||
const context = writable({})
|
||||
|
@ -20,53 +19,28 @@ export const createContextStore = parentContext => {
|
|||
}
|
||||
|
||||
// Provide some data in context
|
||||
const provideData = (providerId, data, scope = ContextScopes.Global) => {
|
||||
const provideData = (providerId, data) => {
|
||||
if (!providerId || data === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
// Provides some action in context
|
||||
const provideAction = (
|
||||
providerId,
|
||||
actionType,
|
||||
callback,
|
||||
scope = ContextScopes.Global
|
||||
) => {
|
||||
const provideAction = (providerId, actionType, callback) => {
|
||||
if (!providerId || !actionType) {
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -74,7 +48,6 @@ export const createContextStore = parentContext => {
|
|||
})
|
||||
broadcastChange(key)
|
||||
}
|
||||
}
|
||||
|
||||
const observeChanges = callback => {
|
||||
observers.push(callback)
|
||||
|
|
|
@ -106,3 +106,8 @@ export const Themes = [
|
|||
export const EventPublishType = {
|
||||
ENV_VAR_UPGRADE_PANEL_OPENED: "environment_variable_upgrade_panel_opened",
|
||||
}
|
||||
|
||||
export const ContextScopes = {
|
||||
Local: "local",
|
||||
Global: "global",
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue