1
0
Fork 0
mirror of synced 2024-10-02 10:08:09 +13:00

Update data binding generation to match how context is provided by components, respecting branching due to local context

This commit is contained in:
Andrew Kingston 2024-02-02 14:59:45 +00:00
parent 41d32fdad1
commit d6bf33bce7
8 changed files with 102 additions and 32 deletions

View file

@ -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 content 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
}

View file

@ -1,6 +1,8 @@
import { cloneDeep } from "lodash/fp"
import { get } from "svelte/store"
import {
buildContextTree,
buildContextTreeLookupMap,
findAllComponents,
findAllMatchingComponents,
findComponent,
@ -20,11 +22,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,20 +218,27 @@ 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
}
if (!map[component._id]) {
map[component._id] = {
component,
definition: def,
contexts: [],
}
// 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
@ -235,7 +246,7 @@ export const getComponentContexts = (
return
}
// Ensure scope matches
let contextScope = context.scope || "global"
let contextScope = context.scope || ContextScopes.Global
if (contextScope !== scope) {
return
}
@ -243,17 +254,23 @@ export const getComponentContexts = (
if (!isContextCompatibleWithComponent(context, component)) {
return
}
if (!map[component._id]) {
map[component._id] = {
component,
definition: def,
contexts: [],
}
}
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) {

View file

@ -4720,7 +4720,8 @@
}
],
"context": {
"type": "schema"
"type": "schema",
"scope": "local"
}
},
"daterangepicker": {

View file

@ -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>

View file

@ -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

View file

@ -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"

View file

@ -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,

View file

@ -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",
}