1 changed files with 85 additions and 0 deletions
@ -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); |
||||||
|
} |
||||||
|
|
||||||
Loading…
Reference in new issue