Integrate actions into new global context and greatly improve performance by only enriching when required
This commit is contained in:
parent
3839e26758
commit
f78cc26a68
2 changed files with 89 additions and 86 deletions
|
@ -35,6 +35,10 @@
|
|||
findHBSBlocks,
|
||||
isJSBinding,
|
||||
} from "@budibase/string-templates"
|
||||
import {
|
||||
getActionContextKey,
|
||||
getActionDependentContextKeys,
|
||||
} from "../utils/buttonActions.js"
|
||||
|
||||
export let instance = {}
|
||||
export let isLayout = false
|
||||
|
@ -165,9 +169,6 @@
|
|||
hasMissingRequiredSettings)
|
||||
$: emptyState = empty && showEmptyState
|
||||
|
||||
// Enrich component settings
|
||||
// $: enrichComponentSettings($context, settingsDefinitionMap)
|
||||
|
||||
// Evaluate conditional UI settings and store any component setting changes
|
||||
// which need to be made
|
||||
$: evaluateConditions(conditions)
|
||||
|
@ -296,18 +297,42 @@
|
|||
}
|
||||
})
|
||||
|
||||
// The known context key map is built up at runtime, as changes to keys are
|
||||
// encountered. We manually seed this to the required action keys as these
|
||||
// are not encountered at runtime and so need computed in advance.
|
||||
knownContextKeyMap = generateActionKeyMap(instance, settingsDefinition)
|
||||
bindingString = bindings.join(" ")
|
||||
|
||||
// Run any migrations
|
||||
runMigrations(instance, settingsDefinition)
|
||||
|
||||
// Force an initial enrichment of the new settings
|
||||
enrichComponentSettings(get(context), settingsDefinitionMap, {
|
||||
force: true,
|
||||
})
|
||||
bindingString = bindings.join(" ")
|
||||
knownContextKeyMap = {}
|
||||
enrichComponentSettings(get(context), settingsDefinitionMap)
|
||||
}
|
||||
|
||||
// Force an initial enrichment of the new settings
|
||||
enrichComponentSettings($context, settingsDefinitionMap)
|
||||
// Extracts a map of all context keys which are required by action settings
|
||||
// to provide the functions to evaluate at runtime. This needs done manually
|
||||
// as the action definitions themselves do not specify bindings for action
|
||||
// keys, meaning we cannot do this while doing the other normal bindings.
|
||||
const generateActionKeyMap = (instance, settingsDefinition) => {
|
||||
let map = {}
|
||||
settingsDefinition.forEach(setting => {
|
||||
if (setting.type === "event") {
|
||||
instance[setting.key]?.forEach(action => {
|
||||
// We depend on the actual action key
|
||||
const actionKey = getActionContextKey(action)
|
||||
if (actionKey) {
|
||||
map[actionKey] = true
|
||||
}
|
||||
|
||||
// We also depend on any manually declared context keys
|
||||
getActionDependentContextKeys(action)?.forEach(key => {
|
||||
map[key] = true
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
return map
|
||||
}
|
||||
|
||||
const runMigrations = (instance, settingsDefinition) => {
|
||||
|
|
|
@ -17,6 +17,54 @@ import { ActionTypes } from "constants"
|
|||
import { enrichDataBindings } from "./enrichDataBinding"
|
||||
import { Helpers } from "@budibase/bbui"
|
||||
|
||||
// Default action handler, which extracts an action from context that was
|
||||
// provided by another component and executes it with all action parameters
|
||||
const contextActionHandler = async (action, context) => {
|
||||
const key = getActionContextKey(action)
|
||||
const fn = context[key]
|
||||
if (fn) {
|
||||
return await fn(action.parameters)
|
||||
}
|
||||
}
|
||||
|
||||
// Generates the context key, which is the key that this action depends on in
|
||||
// context to provide the function it will run. This is broken out as a util
|
||||
// because we reuse this inside the core Component.svelte file to determine
|
||||
// what the required action context keys are for all action settings.
|
||||
export const getActionContextKey = action => {
|
||||
const type = action?.["##eventHandlerType"]
|
||||
const key = (componentId, type) => `${componentId}_${type}`
|
||||
switch (type) {
|
||||
case "Scroll To Field":
|
||||
return key(action.parameters.componentId, ActionTypes.ScrollTo)
|
||||
case "Update Field Value":
|
||||
return key(action.parameters.componentId, ActionTypes.UpdateFieldValue)
|
||||
case "Validate Form":
|
||||
return key(action.parameters.componentId, ActionTypes.ValidateForm)
|
||||
case "Refresh Data Provider":
|
||||
return key(action.parameters.componentId, ActionTypes.RefreshDatasource)
|
||||
case "Clear Form":
|
||||
return key(action.parameters.componentId, ActionTypes.ClearForm)
|
||||
case "Change Form Step":
|
||||
return key(action.parameters.componentId, ActionTypes.ChangeFormStep)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// If button actions depend on context, they must declare which keys they need
|
||||
export const getActionDependentContextKeys = action => {
|
||||
const type = action?.["##eventHandlerType"]
|
||||
switch (type) {
|
||||
case "Save Row":
|
||||
case "Duplicate Row":
|
||||
if (action.parameters?.providerId) {
|
||||
return [action.parameters.providerId]
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
const saveRowHandler = async (action, context) => {
|
||||
const { fields, providerId, tableId, notificationOverride } =
|
||||
action.parameters
|
||||
|
@ -189,17 +237,6 @@ const navigationHandler = action => {
|
|||
routeStore.actions.navigate(url, peek, externalNewTab)
|
||||
}
|
||||
|
||||
const scrollHandler = async (action, context) => {
|
||||
return await executeActionHandler(
|
||||
context,
|
||||
action.parameters.componentId,
|
||||
ActionTypes.ScrollTo,
|
||||
{
|
||||
field: action.parameters.field,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const queryExecutionHandler = async action => {
|
||||
const { datasourceId, queryId, queryParams, notificationOverride } =
|
||||
action.parameters
|
||||
|
@ -235,47 +272,6 @@ const queryExecutionHandler = async action => {
|
|||
}
|
||||
}
|
||||
|
||||
const executeActionHandler = async (
|
||||
context,
|
||||
componentId,
|
||||
actionType,
|
||||
params
|
||||
) => {
|
||||
const fn = context[`${componentId}_${actionType}`]
|
||||
if (fn) {
|
||||
return await fn(params)
|
||||
}
|
||||
}
|
||||
|
||||
const updateFieldValueHandler = async (action, context) => {
|
||||
return await executeActionHandler(
|
||||
context,
|
||||
action.parameters.componentId,
|
||||
ActionTypes.UpdateFieldValue,
|
||||
{
|
||||
type: action.parameters.type,
|
||||
field: action.parameters.field,
|
||||
value: action.parameters.value,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const validateFormHandler = async (action, context) => {
|
||||
return await executeActionHandler(
|
||||
context,
|
||||
action.parameters.componentId,
|
||||
ActionTypes.ValidateForm
|
||||
)
|
||||
}
|
||||
|
||||
const refreshDataProviderHandler = async (action, context) => {
|
||||
return await executeActionHandler(
|
||||
context,
|
||||
action.parameters.componentId,
|
||||
ActionTypes.RefreshDatasource
|
||||
)
|
||||
}
|
||||
|
||||
const logoutHandler = async action => {
|
||||
await authStore.actions.logOut()
|
||||
let redirectUrl = "/builder/auth/login"
|
||||
|
@ -292,23 +288,6 @@ const logoutHandler = async action => {
|
|||
}
|
||||
}
|
||||
|
||||
const clearFormHandler = async (action, context) => {
|
||||
return await executeActionHandler(
|
||||
context,
|
||||
action.parameters.componentId,
|
||||
ActionTypes.ClearForm
|
||||
)
|
||||
}
|
||||
|
||||
const changeFormStepHandler = async (action, context) => {
|
||||
return await executeActionHandler(
|
||||
context,
|
||||
action.parameters.componentId,
|
||||
ActionTypes.ChangeFormStep,
|
||||
action.parameters
|
||||
)
|
||||
}
|
||||
|
||||
const closeScreenModalHandler = action => {
|
||||
let url
|
||||
if (action?.parameters) {
|
||||
|
@ -416,16 +395,10 @@ const handlerMap = {
|
|||
["Duplicate Row"]: duplicateRowHandler,
|
||||
["Delete Row"]: deleteRowHandler,
|
||||
["Navigate To"]: navigationHandler,
|
||||
["Scroll To Field"]: scrollHandler,
|
||||
["Execute Query"]: queryExecutionHandler,
|
||||
["Trigger Automation"]: triggerAutomationHandler,
|
||||
["Validate Form"]: validateFormHandler,
|
||||
["Update Field Value"]: updateFieldValueHandler,
|
||||
["Refresh Data Provider"]: refreshDataProviderHandler,
|
||||
["Log Out"]: logoutHandler,
|
||||
["Clear Form"]: clearFormHandler,
|
||||
["Close Screen Modal"]: closeScreenModalHandler,
|
||||
["Change Form Step"]: changeFormStepHandler,
|
||||
["Update State"]: updateStateHandler,
|
||||
["Upload File to S3"]: s3UploadHandler,
|
||||
["Export Data"]: exportDataHandler,
|
||||
|
@ -460,7 +433,12 @@ export const enrichButtonActions = (actions, context) => {
|
|||
return actions
|
||||
}
|
||||
|
||||
const handlers = actions.map(def => handlerMap[def["##eventHandlerType"]])
|
||||
// Get handlers for each action. If no bespoke handler is configured, fall
|
||||
// back to simply executing this action from context.
|
||||
const handlers = actions.map(def => {
|
||||
return handlerMap[def["##eventHandlerType"]] || contextActionHandler
|
||||
})
|
||||
|
||||
return async eventContext => {
|
||||
// Button context is built up as actions are executed.
|
||||
// Inherit any previous button context which may have come from actions
|
||||
|
|
Loading…
Reference in a new issue