You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
85 lines
2.2 KiB
85 lines
2.2 KiB
/** |
|
* @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); |
|
} |
|
|
|
|