function setByPath(obj: T, path: string, value: unknown): T { const keys = path.split("."); if (keys.length === 1) { return { ...obj, [keys[0]]: value }; } const root = { ...obj } as Record; let current = root; for (let i = 0; i < keys.length - 1; i++) { const key = keys[i]; current[key] = { ...(current[key] as Record) }; current = current[key] as Record; } current[keys[keys.length - 1]] = value; return root as T; } function mergeWithDefaults(attrs: Partial, defaults: T): T { const result = { ...defaults }; for (const key in attrs) { const attrValue = attrs[key]; const defaultValue = defaults[key]; if ( attrValue !== undefined && typeof attrValue === "object" && attrValue !== null && !Array.isArray(attrValue) && typeof defaultValue === "object" && defaultValue !== null && !Array.isArray(defaultValue) ) { result[key] = { ...defaultValue, ...attrValue, } as T[Extract]; } else if (attrValue !== undefined) { result[key] = attrValue as T[Extract]; } } return result; } function applyBindings( attrs: T, bindings: ElementBindings | undefined, previewValues: Record, defaultFallback?: string ): T { if (!bindings) return attrs; let result = attrs; for (const path of Object.keys(bindings)) { const preview = previewValues[path]; if (preview != null) { result = setByPath(result, path, preview); } else if (defaultFallback != null) { result = setByPath(result, path, defaultFallback); } } return result; } export type ElementBindings = Record; export function mergeAttrsWithDefaultsAndApplyBindings( attrs: Partial, defaults: T, bindings: ElementBindings | undefined, previewValues: Record, defaultFallback?: string ): T { const merged = mergeWithDefaults(attrs, defaults); return applyBindings(merged, bindings, previewValues, defaultFallback); }