UNPKG

@razen-core/zenweb

Version:

A minimalist TypeScript framework for building reactive web applications with no virtual DOM

138 lines 4.34 kB
/** * ZenWeb Utility Helpers * Utility functions for conditional rendering, fragments, etc. */ import { createElement } from '../dom.js'; import { effect } from '../state.js'; /** * Fragment - render children without wrapper */ export function fragment(...children) { const flat = children.flat(Infinity).filter(child => child !== null && child !== undefined); return flat.filter(child => child instanceof HTMLElement); } /** * Conditional rendering - show content when condition is true */ export function when(condition, content) { return condition ? content() : null; } /** * Show/hide based on condition with reactive support * Usage: show(state.visible, element) or show(() => state.count > 0, element) * Properly toggles DOM presence (not just display:none) */ export function show(condition, content) { const container = createElement('div', { 'data-conditional': 'true' }); let isAttached = false; const updateVisibility = (visible) => { if (visible && !isAttached) { container.appendChild(content); isAttached = true; } else if (!visible && isAttached) { if (container.contains(content)) { container.removeChild(content); } isAttached = false; } }; if (typeof condition === 'function') { // Reactive condition effect(() => { updateVisibility(condition()); }); } else { // Static condition updateVisibility(condition); } return container; } /** * Iterate over array and render items */ export function each(items, renderFn, keyFn) { return items.map((item, index) => { const element = renderFn(item, index); if (keyFn) { element.dataset.key = String(keyFn(item, index)); } return element; }); } /** * Switch case rendering */ export function switchCase(value, cases, defaultCase) { const key = String(value); if (cases[key]) { return cases[key](); } return defaultCase ? defaultCase() : null; } /** * Dynamic component - render component based on type */ export function dynamic(component, props, ...children) { if (typeof component === 'function') { return component(props); } return createElement(component, props, ...children); } /** * Portal - render content in a different DOM location */ export function portal(children, target) { const container = typeof target === 'string' ? document.querySelector(target) : target; if (container) { const flat = children.flat(Infinity); flat.forEach(child => { if (child instanceof HTMLElement) { container.appendChild(child); } else if (typeof child === 'string' || typeof child === 'number') { container.appendChild(document.createTextNode(String(child))); } }); } return createElement('div', { 'data-portal': true }); } /** * CSS helper - add styles to external stylesheet * Usage: css('.my-class', { color: 'red', fontSize: '16px' }) * Styles are added to a <style> tag in the document head */ let styleSheet = null; let styleElement = null; export function css(selector, styles) { // Create style element if it doesn't exist if (!styleElement) { styleElement = document.createElement('style'); styleElement.setAttribute('data-zenweb-styles', 'true'); document.head.appendChild(styleElement); styleSheet = styleElement.sheet; } if (typeof styles === 'string') { // Raw CSS string const rule = `${selector} { ${styles} }`; if (styleSheet) { styleSheet.insertRule(rule, styleSheet.cssRules.length); } } else { // Style object - convert to CSS string const cssText = Object.entries(styles) .map(([key, value]) => { // Convert camelCase to kebab-case const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase(); return `${cssKey}: ${value}`; }) .join('; '); const rule = `${selector} { ${cssText} }`; if (styleSheet) { styleSheet.insertRule(rule, styleSheet.cssRules.length); } } } //# sourceMappingURL=utilities.js.map