diff --git a/binding.js b/binding.js new file mode 100644 index 0000000..09da00c --- /dev/null +++ b/binding.js @@ -0,0 +1,85 @@ +/** + * @fileoverview Reactive data binding system. + * Creates a deep reactive Proxy that auto-updates DOM elements with data-bind attributes. + */ + +/** + * Gets a nested property value from an object using dot notation. + * @param {Object} obj - The object to get the value from + * @param {string} path - Dot notation path (e.g., "supplies.lemons") + * @returns {*} The value at the path + */ +function getByPath(obj, path) { + return path.split('.').reduce((current, key) => current?.[key], obj); +} + +/** + * Formats a value based on the format type. + * @param {*} value - The value to format + * @param {string} [format] - The format type (e.g., "currency", "number") + * @returns {string} The formatted value + */ +function formatValue(value, format) { + if (format === 'currency') { + return new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD' + }).format(value); + } + + if (format === 'number') { + return new Intl.NumberFormat('en-US').format(value); + } + + return value; +} + +/** + * Updates all DOM elements with data-bind attributes to reflect current state. + * @param {Object} state - The state object to read values from + */ +export function updateBindings(state) { + const elements = document.querySelectorAll('[data-bind]'); + + elements.forEach(el => { + const path = el.dataset.bind; + const format = el.dataset.format; + const value = getByPath(state, path); + + if (value !== undefined) { + el.textContent = formatValue(value, format); + } + }); +} + +/** + * Creates a deep reactive Proxy that auto-updates DOM bindings on change. + * @param {Object} target - The initial state object + * @returns {Proxy} A reactive proxy that updates DOM on changes + */ +export function createReactiveState(target) { + const handler = { + get(obj, prop) { + const value = obj[prop]; + + // If the value is an object, wrap it in a proxy too (deep reactivity) + if (typeof value === 'object' && value !== null) { + return new Proxy(value, handler); + } + + return value; + }, + + set(obj, prop, value) { + obj[prop] = value; + + // Update all bound DOM elements + updateBindings(target); + + return true; + } + }; + + return new Proxy(target, handler); +} +