import { enrichDataBindings } from "./enrichDataBinding" import { enrichButtonActions } from "./buttonActions" import { decodeJSBinding } from "@budibase/string-templates" /** * Deeply compares 2 props using JSON.stringify. * Does not consider functions, as currently only button actions have a function * prop and it's cheaper to just always re-render buttons than it is to deeply * compare them. */ export const propsAreSame = (a, b) => { if (a === b) { return true } if (typeof a === "function" || typeof b === "function") { return false } return JSON.stringify(a) === JSON.stringify(b) } /** * Enriches component props. * Data bindings are enriched, and button actions are enriched. */ export const enrichProps = (props, context, settingsDefinitionMap) => { // We want to exclude any button actions from enrichment at this stage. // Extract top level button action settings. let normalProps = { ...props } let actionProps = {} Object.keys(normalProps).forEach(prop => { if (settingsDefinitionMap?.[prop]?.type === "event") { actionProps[prop] = normalProps[prop] delete normalProps[prop] } }) // Store the original conditions so that we can restore parts of them after // enrichment let rawConditions = normalProps._conditions // Enrich all props except button actions let enrichedProps = enrichDataBindings(normalProps, context) // Enrich button actions. // Actions are enriched into a function at this stage, but actual data // binding enrichment is done dynamically at runtime. Object.keys(actionProps).forEach(prop => { enrichedProps[prop] = enrichButtonActions(actionProps[prop], context) }) // Conditions if (enrichedProps._conditions?.length) { enrichedProps._conditions.forEach((condition, idx) => { if (settingsDefinitionMap?.[condition.setting]?.type === "event") { // Use the original condition action value to enrich it to a button // action condition.settingValue = enrichButtonActions( rawConditions[idx].settingValue, context ) // Since we can't compare functions, we need to assume that conditions // change after every enrichment condition.rand = Math.random() } }) } return enrichedProps } /** * Checks if a props object references a particular context binding. * e.g. if props are { foo: "My name is {{ person.name }}" }, and we search for" + * "person", then this function wil return true - the props do a context key * called "person". * @param props the props object to search * @param bindingKey the key to search for */ export const propsUseBinding = (props, bindingKey) => { if (!Object.keys(props || {}).length) { return false } const string = JSON.stringify(props) const usedInHBS = string.includes(`[${bindingKey}]`) if (usedInHBS) { return true } const jsBindingRegex = new RegExp("{{ js [^}]+ }}", "g") const jsBindings = [...string.matchAll(jsBindingRegex)] for (let jsBinding of jsBindings) { const encoded = jsBinding[0] const js = decodeJSBinding(encoded) if (js?.includes(`$("[${bindingKey}]`)) { return true } } return false }