UNPKG

@uistate/core

Version:

Revolutionary DOM-based state management using CSS custom properties - zero dependencies, potential O(1) updates

169 lines (136 loc) 6.48 kB
const createTemplateManager = (stateManager) => { const manager = { handlers: {}, onAction(action, handler) { this.handlers[action] = handler; return this; }, registerActions(actionsMap) { Object.entries(actionsMap).forEach(([action, handler]) => { if (typeof handler === 'function') { this.onAction(action, handler); } else if (typeof handler === 'object' && handler !== null) { const { fn, extractId = true, idAttribute = 'id' } = handler; if (typeof fn !== 'function') { throw new Error(`Handler for action '${action}' must be a function`); } this.onAction(action, (e) => { if (extractId) { const target = e.target.closest('[data-action]'); const id = target.dataset[idAttribute] || target.dataset.actionId || target.dataset.cardId || target.dataset.itemId; fn(id, e, target); } else { fn(e); } }); } else { throw new Error(`Invalid handler for action '${action}'`); } }); return this; }, attachDelegation(root = document.body) { root.addEventListener('click', e => { const target = e.target.closest('[data-action]'); if (!target) return; const action = target.dataset.action; if (!action) return; const handler = this.handlers[action]; if (typeof handler === 'function') { handler(e); } else if (target.dataset.value !== undefined && stateManager) { stateManager.setState(action, target.dataset.value); } }); return this; }, renderTemplateFromCss(templateName, data = {}) { const cssTemplate = getComputedStyle(document.documentElement) .getPropertyValue(`--template-${templateName}`) .trim() .replace(/^['"]|['"]$/g, ''); if (!cssTemplate) throw new Error(`Template not found in CSS: --template-${templateName}`); let html = cssTemplate; Object.entries(data).forEach(([key, value]) => { const regex = new RegExp(`{{${key}}}`, 'g'); html = html.replace(regex, value); }); const temp = document.createElement('div'); temp.innerHTML = html; return temp.firstElementChild; }, mount(componentName, container) { const tpl = document.getElementById(`${componentName}-template`); if (!tpl) throw new Error(`Template not found: ${componentName}-template`); const clone = tpl.content.cloneNode(true); function resolvePlaceholders(fragment) { Array.from(fragment.querySelectorAll('*')).forEach(el => { const tag = el.tagName.toLowerCase(); if (tag.endsWith('-placeholder')) { const name = tag.replace('-placeholder',''); const childTpl = document.getElementById(`${name}-template`); if (!childTpl) throw new Error(`Template not found: ${name}-template`); const childClone = childTpl.content.cloneNode(true); resolvePlaceholders(childClone); el.replaceWith(childClone); } }); } resolvePlaceholders(clone); container.appendChild(clone); return clone.firstElementChild; }, createComponent(name, renderFn, stateKeys = []) { if (!stateManager) { throw new Error('State manager is required for reactive components'); } let tpl = document.getElementById(`${name}-template`); if (!tpl) { tpl = document.createElement('template'); tpl.id = `${name}-template`; document.body.appendChild(tpl); } tpl.innerHTML = renderFn(stateManager); if (stateKeys.length > 0) { stateKeys.forEach(key => { stateManager.observe(key, () => { tpl.innerHTML = renderFn(stateManager); }); }); } return { mount: (container) => this.mount(name, container) }; }, applyClassesFromState(element, stateKey, options = {}) { if (!element) return element; const { prefix = '', clearExisting = false, namespace = '' } = typeof options === 'string' ? { prefix: options } : options; const prefixPath = prefix ? `${prefix}-` : ''; const namespacePath = namespace ? `${namespace}-` : ''; const classString = getComputedStyle(document.documentElement) .getPropertyValue(`--${namespacePath}${stateKey}-classes`) .trim() .replace(/^['"]|['"]$/g, ''); if (classString) { if (clearExisting) { element.className = ''; } classString.split(' ').forEach(cls => { if (cls) element.classList.add(cls); }); } return element; } }; return manager; }; const TemplateManager = createTemplateManager(); export default createTemplateManager; export { createTemplateManager, TemplateManager };