2021-01-19 18:38:24 +01:00
|
|
|
import { cloneDeep } from "lodash/fp"
|
2021-01-14 16:39:50 +01:00
|
|
|
import { get } from "svelte/store"
|
2021-03-16 20:11:00 +01:00
|
|
|
import { findComponent, findComponentPath } from "./storeUtils"
|
2021-03-23 12:16:54 +01:00
|
|
|
import { store } from "builderStore"
|
2021-04-13 17:57:22 +02:00
|
|
|
import { tables as tablesStore, queries as queriesStores } from "stores/backend"
|
2021-01-22 18:57:38 +01:00
|
|
|
import { makePropSafe } from "@budibase/string-templates"
|
2021-01-28 15:29:35 +01:00
|
|
|
import { TableNames } from "../constants"
|
2021-01-14 16:39:50 +01:00
|
|
|
|
2021-01-21 12:31:45 +01:00
|
|
|
// Regex to match all instances of template strings
|
|
|
|
const CAPTURE_VAR_INSIDE_TEMPLATE = /{{([^}]+)}}/g
|
2021-02-22 16:49:57 +01:00
|
|
|
const CAPTURE_HBS_TEMPLATE = /{{[\S\s]*?}}/g
|
2021-01-19 16:39:04 +01:00
|
|
|
|
2021-01-14 16:39:50 +01:00
|
|
|
/**
|
|
|
|
* Gets all bindable data context fields and instance fields.
|
|
|
|
*/
|
2021-02-15 21:03:29 +01:00
|
|
|
export const getBindableProperties = (asset, componentId) => {
|
|
|
|
const contextBindings = getContextBindings(asset, componentId)
|
|
|
|
const userBindings = getUserBindings()
|
|
|
|
const urlBindings = getUrlBindings(asset, componentId)
|
|
|
|
return [...contextBindings, ...userBindings, ...urlBindings]
|
2021-01-14 16:39:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-01-19 18:38:24 +01:00
|
|
|
* Gets all data provider components above a component.
|
2021-01-14 16:39:50 +01:00
|
|
|
*/
|
2021-02-15 21:03:29 +01:00
|
|
|
export const getDataProviderComponents = (asset, componentId) => {
|
|
|
|
if (!asset || !componentId) {
|
2021-01-14 16:39:50 +01:00
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
2021-01-19 16:39:04 +01:00
|
|
|
// Get the component tree leading up to this component, ignoring the component
|
|
|
|
// itself
|
2021-02-15 21:03:29 +01:00
|
|
|
const path = findComponentPath(asset.props, componentId)
|
2021-01-14 16:39:50 +01:00
|
|
|
path.pop()
|
|
|
|
|
2021-01-19 18:38:24 +01:00
|
|
|
// Filter by only data provider components
|
2021-05-03 09:31:09 +02:00
|
|
|
return path.filter((component) => {
|
2021-01-19 18:38:24 +01:00
|
|
|
const def = store.actions.components.getDefinition(component._component)
|
2021-03-29 17:37:17 +02:00
|
|
|
return def?.context != null
|
2021-01-19 18:38:24 +01:00
|
|
|
})
|
|
|
|
}
|
2021-01-14 16:39:50 +01:00
|
|
|
|
2021-02-01 19:51:22 +01:00
|
|
|
/**
|
|
|
|
* Gets all data provider components above a component.
|
|
|
|
*/
|
2021-02-15 21:03:29 +01:00
|
|
|
export const getActionProviderComponents = (asset, componentId, actionType) => {
|
|
|
|
if (!asset || !componentId) {
|
2021-02-01 19:51:22 +01:00
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the component tree leading up to this component, ignoring the component
|
|
|
|
// itself
|
2021-02-15 21:03:29 +01:00
|
|
|
const path = findComponentPath(asset.props, componentId)
|
2021-02-01 19:51:22 +01:00
|
|
|
path.pop()
|
|
|
|
|
|
|
|
// Filter by only data provider components
|
2021-05-03 09:31:09 +02:00
|
|
|
return path.filter((component) => {
|
2021-02-01 19:51:22 +01:00
|
|
|
const def = store.actions.components.getDefinition(component._component)
|
|
|
|
return def?.actions?.includes(actionType)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-01-19 18:38:24 +01:00
|
|
|
/**
|
|
|
|
* Gets a datasource object for a certain data provider component
|
|
|
|
*/
|
2021-03-18 18:34:51 +01:00
|
|
|
export const getDatasourceForProvider = (asset, component) => {
|
2021-01-19 18:38:24 +01:00
|
|
|
const def = store.actions.components.getDefinition(component?._component)
|
|
|
|
if (!def) {
|
|
|
|
return null
|
|
|
|
}
|
2021-01-19 16:39:04 +01:00
|
|
|
|
2021-03-18 18:34:51 +01:00
|
|
|
// If this component has a dataProvider setting, go up the stack and use it
|
2021-05-03 09:31:09 +02:00
|
|
|
const dataProviderSetting = def.settings.find((setting) => {
|
2021-03-18 18:34:51 +01:00
|
|
|
return setting.type === "dataProvider"
|
|
|
|
})
|
|
|
|
if (dataProviderSetting) {
|
|
|
|
const settingValue = component[dataProviderSetting.key]
|
2021-03-22 18:57:19 +01:00
|
|
|
const providerId = extractLiteralHandlebarsID(settingValue)
|
|
|
|
const provider = findComponent(asset.props, providerId)
|
|
|
|
return getDatasourceForProvider(asset, provider)
|
2021-03-18 18:34:51 +01:00
|
|
|
}
|
|
|
|
|
2021-01-19 18:38:24 +01:00
|
|
|
// Extract datasource from component instance
|
2021-03-16 20:11:00 +01:00
|
|
|
const validSettingTypes = ["dataSource", "table", "schema"]
|
2021-05-03 09:31:09 +02:00
|
|
|
const datasourceSetting = def.settings.find((setting) => {
|
2021-02-04 19:11:56 +01:00
|
|
|
return validSettingTypes.includes(setting.type)
|
2021-01-19 18:38:24 +01:00
|
|
|
})
|
|
|
|
if (!datasourceSetting) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
// There are different types of setting which can be a datasource, for
|
|
|
|
// example an actual datasource object, or a table ID string.
|
|
|
|
// Convert the datasource setting into a proper datasource object so that
|
|
|
|
// we can use it properly
|
2021-02-04 19:11:56 +01:00
|
|
|
if (datasourceSetting.type === "table") {
|
2021-01-19 18:38:24 +01:00
|
|
|
return {
|
|
|
|
tableId: component[datasourceSetting?.key],
|
|
|
|
type: "table",
|
2021-01-19 16:39:04 +01:00
|
|
|
}
|
2021-02-04 19:11:56 +01:00
|
|
|
} else {
|
|
|
|
return component[datasourceSetting?.key]
|
2021-01-19 18:38:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-02-15 21:03:29 +01:00
|
|
|
* Gets all bindable data properties from component data contexts.
|
2021-01-19 18:38:24 +01:00
|
|
|
*/
|
2021-02-15 21:03:29 +01:00
|
|
|
const getContextBindings = (asset, componentId) => {
|
2021-01-19 18:38:24 +01:00
|
|
|
// Extract any components which provide data contexts
|
2021-02-15 21:03:29 +01:00
|
|
|
const dataProviders = getDataProviderComponents(asset, componentId)
|
|
|
|
let bindings = []
|
2021-01-19 16:39:04 +01:00
|
|
|
|
2021-02-04 16:11:05 +01:00
|
|
|
// Create bindings for each data provider
|
2021-05-03 09:31:09 +02:00
|
|
|
dataProviders.forEach((component) => {
|
2021-03-16 14:54:34 +01:00
|
|
|
const def = store.actions.components.getDefinition(component._component)
|
2021-03-29 17:37:17 +02:00
|
|
|
const contextDefinition = def.context
|
2021-03-16 14:54:34 +01:00
|
|
|
let schema
|
2021-03-16 20:11:00 +01:00
|
|
|
let readablePrefix
|
2021-02-04 14:01:49 +01:00
|
|
|
|
2021-03-16 14:54:34 +01:00
|
|
|
if (contextDefinition.type === "form") {
|
2021-03-29 17:37:17 +02:00
|
|
|
// Forms do not need table schemas
|
|
|
|
// Their schemas are built from their component field names
|
2021-02-04 16:11:05 +01:00
|
|
|
schema = buildFormSchema(component)
|
2021-03-16 20:11:00 +01:00
|
|
|
readablePrefix = "Fields"
|
|
|
|
} else if (contextDefinition.type === "static") {
|
2021-03-29 17:37:17 +02:00
|
|
|
// Static contexts are fully defined by the components
|
2021-03-16 20:11:00 +01:00
|
|
|
schema = {}
|
|
|
|
const values = contextDefinition.values || []
|
2021-05-03 09:31:09 +02:00
|
|
|
values.forEach((value) => {
|
2021-03-16 20:11:00 +01:00
|
|
|
schema[value.key] = { name: value.label, type: "string" }
|
|
|
|
})
|
|
|
|
} else if (contextDefinition.type === "schema") {
|
2021-03-29 17:37:17 +02:00
|
|
|
// Schema contexts are generated dynamically depending on their data
|
|
|
|
const datasource = getDatasourceForProvider(asset, component)
|
2021-02-04 16:11:05 +01:00
|
|
|
if (!datasource) {
|
|
|
|
return
|
|
|
|
}
|
2021-03-16 20:11:00 +01:00
|
|
|
const info = getSchemaForDatasource(datasource)
|
2021-02-04 16:11:05 +01:00
|
|
|
schema = info.schema
|
2021-03-16 20:11:00 +01:00
|
|
|
readablePrefix = info.table?.name
|
2021-02-04 16:11:05 +01:00
|
|
|
}
|
2021-03-16 20:11:00 +01:00
|
|
|
if (!schema) {
|
2021-02-04 16:11:05 +01:00
|
|
|
return
|
2021-01-21 11:40:45 +01:00
|
|
|
}
|
2021-02-04 16:11:05 +01:00
|
|
|
|
2021-01-15 15:47:36 +01:00
|
|
|
const keys = Object.keys(schema).sort()
|
2021-01-19 18:38:24 +01:00
|
|
|
|
|
|
|
// Create bindable properties for each schema field
|
2021-03-22 13:10:43 +01:00
|
|
|
const safeComponentId = makePropSafe(component._id)
|
2021-05-03 09:31:09 +02:00
|
|
|
keys.forEach((key) => {
|
2021-01-15 15:47:36 +01:00
|
|
|
const fieldSchema = schema[key]
|
2021-03-16 20:11:00 +01:00
|
|
|
|
|
|
|
// Make safe runtime binding and replace certain bindings with a
|
|
|
|
// new property to help display components
|
2021-01-14 16:39:50 +01:00
|
|
|
let runtimeBoundKey = key
|
2021-01-15 15:47:36 +01:00
|
|
|
if (fieldSchema.type === "link") {
|
2021-02-19 13:00:06 +01:00
|
|
|
runtimeBoundKey = `${key}_text`
|
2021-01-15 15:47:36 +01:00
|
|
|
} else if (fieldSchema.type === "attachment") {
|
2021-01-14 16:39:50 +01:00
|
|
|
runtimeBoundKey = `${key}_first`
|
|
|
|
}
|
2021-03-22 13:10:43 +01:00
|
|
|
const runtimeBinding = `${safeComponentId}.${makePropSafe(
|
|
|
|
runtimeBoundKey
|
|
|
|
)}`
|
2021-03-16 20:11:00 +01:00
|
|
|
|
|
|
|
// Optionally use a prefix with readable bindings
|
|
|
|
let readableBinding = component._instanceName
|
|
|
|
if (readablePrefix) {
|
|
|
|
readableBinding += `.${readablePrefix}`
|
|
|
|
}
|
|
|
|
readableBinding += `.${fieldSchema.name || key}`
|
2021-01-14 16:39:50 +01:00
|
|
|
|
2021-03-16 20:11:00 +01:00
|
|
|
// Create the binding object
|
2021-02-15 21:03:29 +01:00
|
|
|
bindings.push({
|
2021-01-14 16:39:50 +01:00
|
|
|
type: "context",
|
2021-03-16 20:11:00 +01:00
|
|
|
runtimeBinding,
|
|
|
|
readableBinding,
|
2021-02-04 16:11:05 +01:00
|
|
|
// Field schema and provider are required to construct relationship
|
|
|
|
// datasource options, based on bindable properties
|
2021-01-15 15:47:36 +01:00
|
|
|
fieldSchema,
|
2021-01-19 18:38:24 +01:00
|
|
|
providerId: component._id,
|
2021-01-14 16:39:50 +01:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
2021-01-28 15:29:35 +01:00
|
|
|
|
2021-02-15 21:03:29 +01:00
|
|
|
return bindings
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets all bindable properties from the logged in user.
|
|
|
|
*/
|
|
|
|
const getUserBindings = () => {
|
|
|
|
let bindings = []
|
2021-03-23 14:31:56 +01:00
|
|
|
const { schema } = getSchemaForDatasource({
|
2021-03-19 15:04:00 +01:00
|
|
|
type: "table",
|
|
|
|
tableId: TableNames.USERS,
|
|
|
|
})
|
2021-01-28 15:29:35 +01:00
|
|
|
const keys = Object.keys(schema).sort()
|
2021-03-22 13:10:43 +01:00
|
|
|
const safeUser = makePropSafe("user")
|
2021-05-03 09:31:09 +02:00
|
|
|
keys.forEach((key) => {
|
2021-01-28 15:29:35 +01:00
|
|
|
const fieldSchema = schema[key]
|
|
|
|
// Replace certain bindings with a new property to help display components
|
|
|
|
let runtimeBoundKey = key
|
|
|
|
if (fieldSchema.type === "link") {
|
2021-02-19 13:00:06 +01:00
|
|
|
runtimeBoundKey = `${key}_text`
|
2021-01-28 15:29:35 +01:00
|
|
|
} else if (fieldSchema.type === "attachment") {
|
|
|
|
runtimeBoundKey = `${key}_first`
|
|
|
|
}
|
|
|
|
|
2021-02-15 21:03:29 +01:00
|
|
|
bindings.push({
|
2021-01-28 15:29:35 +01:00
|
|
|
type: "context",
|
2021-03-22 13:10:43 +01:00
|
|
|
runtimeBinding: `${safeUser}.${makePropSafe(runtimeBoundKey)}`,
|
2021-01-28 15:29:35 +01:00
|
|
|
readableBinding: `Current User.${key}`,
|
2021-02-04 16:11:05 +01:00
|
|
|
// Field schema and provider are required to construct relationship
|
|
|
|
// datasource options, based on bindable properties
|
2021-01-28 15:29:35 +01:00
|
|
|
fieldSchema,
|
|
|
|
providerId: "user",
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2021-02-15 21:03:29 +01:00
|
|
|
return bindings
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets all bindable properties from URL parameters.
|
|
|
|
*/
|
2021-05-03 09:31:09 +02:00
|
|
|
const getUrlBindings = (asset) => {
|
2021-02-15 21:03:29 +01:00
|
|
|
const url = asset?.routing?.route ?? ""
|
|
|
|
const split = url.split("/")
|
|
|
|
let params = []
|
2021-05-03 09:31:09 +02:00
|
|
|
split.forEach((part) => {
|
2021-02-15 21:03:29 +01:00
|
|
|
if (part.startsWith(":") && part.length > 1) {
|
|
|
|
params.push(part.replace(/:/g, "").replace(/\?/g, ""))
|
|
|
|
}
|
|
|
|
})
|
2021-03-22 13:10:43 +01:00
|
|
|
const safeURL = makePropSafe("url")
|
2021-05-03 09:31:09 +02:00
|
|
|
return params.map((param) => ({
|
2021-02-15 21:03:29 +01:00
|
|
|
type: "context",
|
2021-03-22 13:10:43 +01:00
|
|
|
runtimeBinding: `${safeURL}.${makePropSafe(param)}`,
|
2021-02-15 21:03:29 +01:00
|
|
|
readableBinding: `URL.${param}`,
|
|
|
|
}))
|
2021-01-14 16:39:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets a schema for a datasource object.
|
|
|
|
*/
|
2021-02-04 14:01:49 +01:00
|
|
|
export const getSchemaForDatasource = (datasource, isForm = false) => {
|
2021-01-19 18:38:24 +01:00
|
|
|
let schema, table
|
|
|
|
if (datasource) {
|
|
|
|
const { type } = datasource
|
2021-01-21 11:40:45 +01:00
|
|
|
if (type === "query") {
|
2021-04-13 10:20:30 +02:00
|
|
|
const queries = get(queriesStores).list
|
2021-05-03 09:31:09 +02:00
|
|
|
table = queries.find((query) => query._id === datasource._id)
|
2021-01-21 11:40:45 +01:00
|
|
|
} else {
|
2021-03-23 12:48:54 +01:00
|
|
|
const tables = get(tablesStore).list
|
2021-05-03 09:31:09 +02:00
|
|
|
table = tables.find((table) => table._id === datasource.tableId)
|
2021-01-21 11:40:45 +01:00
|
|
|
}
|
2021-01-19 18:38:24 +01:00
|
|
|
if (table) {
|
2021-01-21 11:40:45 +01:00
|
|
|
if (type === "view") {
|
2021-01-19 18:38:24 +01:00
|
|
|
schema = cloneDeep(table.views?.[datasource.name]?.schema)
|
2021-02-04 14:01:49 +01:00
|
|
|
} else if (type === "query" && isForm) {
|
|
|
|
schema = {}
|
|
|
|
const params = table.parameters || []
|
2021-05-03 09:31:09 +02:00
|
|
|
params.forEach((param) => {
|
2021-02-11 10:17:36 +01:00
|
|
|
if (param?.name) {
|
|
|
|
schema[param.name] = { ...param, type: "string" }
|
|
|
|
}
|
2021-02-04 14:01:49 +01:00
|
|
|
})
|
2021-01-21 11:40:45 +01:00
|
|
|
} else {
|
2021-01-19 18:38:24 +01:00
|
|
|
schema = cloneDeep(table.schema)
|
|
|
|
}
|
2021-01-14 16:39:50 +01:00
|
|
|
}
|
2021-03-19 15:04:00 +01:00
|
|
|
|
|
|
|
// Add _id and _rev fields for certain types
|
2021-03-22 18:59:54 +01:00
|
|
|
if (schema && !isForm && ["table", "link"].includes(datasource.type)) {
|
2021-03-19 15:04:00 +01:00
|
|
|
schema["_id"] = { type: "string" }
|
|
|
|
schema["_rev"] = { type: "string" }
|
|
|
|
}
|
|
|
|
|
2021-05-04 12:04:42 +02:00
|
|
|
// Ensure there are "name" properties for all fields and that field schema
|
|
|
|
// are objects
|
|
|
|
let fixedSchema = {}
|
|
|
|
Object.entries(schema || {}).forEach(([fieldName, fieldSchema]) => {
|
|
|
|
if (typeof fieldSchema === "string") {
|
|
|
|
fixedSchema[fieldName] = {
|
|
|
|
type: fieldSchema,
|
|
|
|
name: fieldName,
|
2021-03-19 15:04:00 +01:00
|
|
|
}
|
2021-05-04 12:04:42 +02:00
|
|
|
} else {
|
|
|
|
fixedSchema[fieldName] = {
|
|
|
|
...fieldSchema,
|
|
|
|
name: fieldName,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
schema = fixedSchema
|
2021-01-14 16:39:50 +01:00
|
|
|
}
|
|
|
|
return { schema, table }
|
|
|
|
}
|
2021-01-19 16:39:04 +01:00
|
|
|
|
2021-02-04 14:01:49 +01:00
|
|
|
/**
|
|
|
|
* Builds a form schema given a form component.
|
|
|
|
* A form schema is a schema of all the fields nested anywhere within a form.
|
|
|
|
*/
|
2021-05-03 09:31:09 +02:00
|
|
|
const buildFormSchema = (component) => {
|
2021-02-04 14:01:49 +01:00
|
|
|
let schema = {}
|
|
|
|
if (!component) {
|
|
|
|
return schema
|
|
|
|
}
|
|
|
|
const def = store.actions.components.getDefinition(component._component)
|
|
|
|
const fieldSetting = def?.settings?.find(
|
2021-05-03 09:31:09 +02:00
|
|
|
(setting) => setting.key === "field" && setting.type.startsWith("field/")
|
2021-02-04 14:01:49 +01:00
|
|
|
)
|
|
|
|
if (fieldSetting && component.field) {
|
|
|
|
const type = fieldSetting.type.split("field/")[1]
|
|
|
|
if (type) {
|
2021-03-16 20:11:00 +01:00
|
|
|
schema[component.field] = { type }
|
2021-02-04 14:01:49 +01:00
|
|
|
}
|
|
|
|
}
|
2021-05-03 09:31:09 +02:00
|
|
|
component._children?.forEach((child) => {
|
2021-02-04 14:01:49 +01:00
|
|
|
const childSchema = buildFormSchema(child)
|
|
|
|
schema = { ...schema, ...childSchema }
|
|
|
|
})
|
|
|
|
return schema
|
|
|
|
}
|
|
|
|
|
2021-02-22 16:49:57 +01:00
|
|
|
/**
|
|
|
|
* Recurses the input object to remove any instances of bindings.
|
|
|
|
*/
|
|
|
|
export function removeBindings(obj) {
|
|
|
|
for (let [key, value] of Object.entries(obj)) {
|
|
|
|
if (typeof value === "object") {
|
|
|
|
obj[key] = removeBindings(value)
|
|
|
|
} else if (typeof value === "string") {
|
|
|
|
obj[key] = value.replace(CAPTURE_HBS_TEMPLATE, "Invalid binding")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return obj
|
|
|
|
}
|
|
|
|
|
2021-01-19 16:39:04 +01:00
|
|
|
/**
|
2021-01-29 21:03:09 +01:00
|
|
|
* utility function for the readableToRuntimeBinding and runtimeToReadableBinding.
|
2021-01-19 16:39:04 +01:00
|
|
|
*/
|
2021-01-29 21:03:09 +01:00
|
|
|
function bindingReplacement(bindableProperties, textWithBindings, convertTo) {
|
2021-01-30 03:54:52 +01:00
|
|
|
const convertFrom =
|
|
|
|
convertTo === "runtimeBinding" ? "readableBinding" : "runtimeBinding"
|
2021-01-19 18:38:24 +01:00
|
|
|
if (typeof textWithBindings !== "string") {
|
|
|
|
return textWithBindings
|
|
|
|
}
|
2021-01-29 21:03:09 +01:00
|
|
|
const convertFromProps = bindableProperties
|
2021-05-03 09:31:09 +02:00
|
|
|
.map((el) => el[convertFrom])
|
2021-01-29 21:03:09 +01:00
|
|
|
.sort((a, b) => {
|
|
|
|
return b.length - a.length
|
|
|
|
})
|
2021-01-21 12:31:45 +01:00
|
|
|
const boundValues = textWithBindings.match(CAPTURE_VAR_INSIDE_TEMPLATE) || []
|
2021-01-19 16:39:04 +01:00
|
|
|
let result = textWithBindings
|
2021-01-26 16:59:28 +01:00
|
|
|
for (let boundValue of boundValues) {
|
2021-01-29 21:03:09 +01:00
|
|
|
let newBoundValue = boundValue
|
|
|
|
for (let from of convertFromProps) {
|
|
|
|
if (newBoundValue.includes(from)) {
|
2021-05-03 09:31:09 +02:00
|
|
|
const binding = bindableProperties.find(
|
|
|
|
(el) => el[convertFrom] === from
|
|
|
|
)
|
2021-01-30 03:54:52 +01:00
|
|
|
newBoundValue = newBoundValue.replace(from, binding[convertTo])
|
2021-01-29 21:03:09 +01:00
|
|
|
}
|
2021-01-19 16:39:04 +01:00
|
|
|
}
|
2021-01-26 16:59:58 +01:00
|
|
|
result = result.replace(boundValue, newBoundValue)
|
2021-01-26 16:59:28 +01:00
|
|
|
}
|
2021-01-19 16:39:04 +01:00
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2021-03-22 18:57:19 +01:00
|
|
|
/**
|
|
|
|
* Extracts a component ID from a handlebars expression setting of
|
|
|
|
* {{ literal [componentId] }}
|
|
|
|
*/
|
|
|
|
function extractLiteralHandlebarsID(value) {
|
2021-03-29 19:21:57 +02:00
|
|
|
return value?.match(/{{\s*literal[\s[]+([a-fA-F0-9]+)[\s\]]*}}/)?.[1]
|
2021-03-22 18:57:19 +01:00
|
|
|
}
|
|
|
|
|
2021-01-29 21:03:09 +01:00
|
|
|
/**
|
|
|
|
* Converts a readable data binding into a runtime data binding
|
|
|
|
*/
|
|
|
|
export function readableToRuntimeBinding(bindableProperties, textWithBindings) {
|
2021-01-30 03:54:52 +01:00
|
|
|
return bindingReplacement(
|
|
|
|
bindableProperties,
|
|
|
|
textWithBindings,
|
|
|
|
"runtimeBinding"
|
|
|
|
)
|
2021-01-29 21:03:09 +01:00
|
|
|
}
|
|
|
|
|
2021-01-19 16:39:04 +01:00
|
|
|
/**
|
|
|
|
* Converts a runtime data binding into a readable data binding
|
|
|
|
*/
|
|
|
|
export function runtimeToReadableBinding(bindableProperties, textWithBindings) {
|
2021-01-30 03:54:52 +01:00
|
|
|
return bindingReplacement(
|
|
|
|
bindableProperties,
|
|
|
|
textWithBindings,
|
|
|
|
"readableBinding"
|
|
|
|
)
|
2021-01-19 16:39:04 +01:00
|
|
|
}
|