UNPKG

muspe-cli

Version:

MusPE Advanced Framework v2.1.3 - Mobile User-friendly Simple Progressive Engine with Enhanced CLI Tools, Specialized E-Commerce Templates, Material Design 3, Progressive Enhancement, Mobile Optimizations, Performance Analysis, and Enterprise-Grade Develo

364 lines (308 loc) 8.53 kB
// MusPE UI - Mobile Toolbar Component class Toolbar { constructor(options = {}) { this.options = { position: 'bottom', // top, bottom items: [], theme: 'light', fixed: true, safeArea: true, badge: {}, ...options }; this.element = null; this.activeIndex = options.activeIndex || 0; this.callbacks = { onItemClick: null, ...options.callbacks }; } render() { this.element = document.createElement('div'); this.element.className = this.buildClasses(); this.element.innerHTML = ` <div class="muspe-toolbar__content"> ${this.renderItems()} </div> `; this.attachEvents(); return this.element; } buildClasses() { const classes = ['muspe-toolbar']; classes.push(`muspe-toolbar--${this.options.position}`); classes.push(`muspe-toolbar--${this.options.theme}`); if (this.options.fixed) { classes.push('muspe-toolbar--fixed'); } if (this.options.safeArea) { classes.push('muspe-safe-area'); } return classes.join(' '); } renderItems() { return this.options.items.map((item, index) => { const isActive = index === this.activeIndex; const badge = this.options.badge[index]; return ` <button class="muspe-toolbar__item ${isActive ? 'muspe-toolbar__item--active' : ''}" data-index="${index}"> <div class="muspe-toolbar__icon-container"> ${this.renderIcon(item.icon, isActive)} ${badge ? `<span class="muspe-toolbar__badge">${badge}</span>` : ''} </div> ${item.label ? `<span class="muspe-toolbar__label">${item.label}</span>` : ''} </button> `; }).join(''); } renderIcon(icon, isActive) { if (typeof icon === 'string') { // If it's a string, treat as HTML or emoji return `<span class="muspe-toolbar__icon">${icon}</span>`; } else if (icon && icon.active && icon.inactive) { // If it has active/inactive states return `<span class="muspe-toolbar__icon">${isActive ? icon.active : icon.inactive}</span>`; } else if (icon && icon.svg) { // If it's an SVG icon return `<span class="muspe-toolbar__icon">${icon.svg}</span>`; } return `<span class="muspe-toolbar__icon">•</span>`; } attachEvents() { if (!this.element) return; this.element.addEventListener('click', (e) => { const item = e.target.closest('.muspe-toolbar__item'); if (!item) return; const index = parseInt(item.dataset.index); this.setActive(index); if (this.callbacks.onItemClick) { this.callbacks.onItemClick(index, this.options.items[index]); } // Add press feedback item.classList.add('muspe-toolbar__item--pressed'); setTimeout(() => { item.classList.remove('muspe-toolbar__item--pressed'); }, 150); }); } setActive(index) { if (index < 0 || index >= this.options.items.length) return; // Remove active class from current item const currentActive = this.element?.querySelector('.muspe-toolbar__item--active'); if (currentActive) { currentActive.classList.remove('muspe-toolbar__item--active'); } // Add active class to new item const newActive = this.element?.querySelector(`[data-index="${index}"]`); if (newActive) { newActive.classList.add('muspe-toolbar__item--active'); } this.activeIndex = index; } setBadge(index, value) { if (!this.options.badge) { this.options.badge = {}; } this.options.badge[index] = value; // Update the badge in DOM const item = this.element?.querySelector(`[data-index="${index}"]`); if (item) { let badge = item.querySelector('.muspe-toolbar__badge'); if (value) { if (!badge) { badge = document.createElement('span'); badge.className = 'muspe-toolbar__badge'; item.querySelector('.muspe-toolbar__icon-container').appendChild(badge); } badge.textContent = value; } else if (badge) { badge.remove(); } } } updateItems(items) { this.options.items = items; if (this.element) { const content = this.element.querySelector('.muspe-toolbar__content'); if (content) { content.innerHTML = this.renderItems(); } } } show() { if (this.element) { if (this.options.position === 'bottom') { this.element.style.transform = 'translateY(0)'; } else { this.element.style.transform = 'translateY(0)'; } } } hide() { if (this.element) { if (this.options.position === 'bottom') { this.element.style.transform = 'translateY(100%)'; } else { this.element.style.transform = 'translateY(-100%)'; } } } destroy() { if (this.element && this.element.parentNode) { this.element.parentNode.removeChild(this.element); } this.element = null; } } // CSS Styles for Toolbar const toolbarStyles = ` .muspe-toolbar { display: flex; background: var(--muspe-white); border-top: 1px solid var(--muspe-gray-200); transition: transform var(--muspe-transition-normal); z-index: var(--muspe-z-dropdown); } .muspe-toolbar--fixed { position: fixed; left: 0; right: 0; } .muspe-toolbar--top { top: 0; border-top: none; border-bottom: 1px solid var(--muspe-gray-200); } .muspe-toolbar--bottom { bottom: 0; } .muspe-toolbar--dark { background: var(--muspe-gray-900); border-top-color: var(--muspe-gray-700); color: var(--muspe-white); } .muspe-toolbar__content { display: flex; width: 100%; padding-bottom: env(safe-area-inset-bottom); } .muspe-toolbar__item { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--muspe-space-2) var(--muspe-space-1); min-height: 56px; background: transparent; border: none; color: var(--muspe-gray-500); cursor: pointer; transition: all var(--muspe-transition-fast); user-select: none; position: relative; } .muspe-toolbar__item:active, .muspe-toolbar__item--pressed { background: var(--muspe-gray-100); transform: scale(0.95); } .muspe-toolbar__item--active { color: var(--muspe-primary); } .muspe-toolbar--dark .muspe-toolbar__item { color: var(--muspe-gray-400); } .muspe-toolbar--dark .muspe-toolbar__item:active, .muspe-toolbar--dark .muspe-toolbar__item--pressed { background: var(--muspe-gray-800); } .muspe-toolbar--dark .muspe-toolbar__item--active { color: var(--muspe-primary); } .muspe-toolbar__icon-container { position: relative; display: flex; align-items: center; justify-content: center; margin-bottom: var(--muspe-space-1); } .muspe-toolbar__icon { font-size: 24px; line-height: 1; display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; } .muspe-toolbar__icon svg { width: 24px; height: 24px; stroke: currentColor; } .muspe-toolbar__label { font-size: var(--muspe-font-size-xs); font-weight: 500; line-height: 1; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } .muspe-toolbar__badge { position: absolute; top: -6px; right: -6px; background: var(--muspe-error); color: var(--muspe-white); font-size: 10px; font-weight: 600; line-height: 1; padding: 2px 6px; border-radius: 10px; min-width: 16px; height: 16px; display: flex; align-items: center; justify-content: center; } /* Responsive adjustments */ @media (max-width: 380px) { .muspe-toolbar__item { padding: var(--muspe-space-1); min-height: 52px; } .muspe-toolbar__icon { font-size: 22px; width: 22px; height: 22px; } .muspe-toolbar__icon svg { width: 22px; height: 22px; } .muspe-toolbar__label { font-size: 10px; } } /* Safe area support */ @supports (padding: max(0px)) { .muspe-toolbar__content { padding-bottom: max(env(safe-area-inset-bottom), var(--muspe-space-2)); } } /* Animation states */ .muspe-toolbar { transform: translateY(0); } `; // Auto-inject styles if (typeof document !== 'undefined') { const styleSheet = document.createElement('style'); styleSheet.textContent = toolbarStyles; document.head.appendChild(styleSheet); } export default Toolbar;