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

385 lines (315 loc) 8.7 kB
// MusPE UI - Modern Button Component class Button { constructor(options = {}) { this.options = { text: 'Button', type: 'button', // button, submit, reset variant: 'primary', // primary, secondary, outline, ghost, danger size: 'md', // sm, md, lg, xl disabled: false, loading: false, fullWidth: false, icon: null, iconPosition: 'left', // left, right hapticFeedback: true, ...options }; this.element = null; this.loadingElement = null; this.callbacks = { onClick: null, ...options.callbacks }; } render() { this.element = document.createElement('button'); this.element.type = this.options.type; this.element.className = this.buildClasses(); this.element.disabled = this.options.disabled || this.options.loading; this.element.innerHTML = this.buildContent(); this.attachEvents(); return this.element; } buildClasses() { const classes = ['muspe-button']; classes.push(`muspe-button--${this.options.variant}`); classes.push(`muspe-button--${this.options.size}`); if (this.options.fullWidth) { classes.push('muspe-button--full'); } if (this.options.disabled) { classes.push('muspe-button--disabled'); } if (this.options.loading) { classes.push('muspe-button--loading'); } return classes.join(' '); } buildContent() { let content = ''; if (this.options.loading) { content += `<span class="muspe-button__spinner"></span>`; } if (this.options.icon && this.options.iconPosition === 'left') { content += `<span class="muspe-button__icon muspe-button__icon--left">${this.options.icon}</span>`; } content += `<span class="muspe-button__text">${this.options.text}</span>`; if (this.options.icon && this.options.iconPosition === 'right') { content += `<span class="muspe-button__icon muspe-button__icon--right">${this.options.icon}</span>`; } return content; } attachEvents() { if (!this.element) return; // Click handler this.element.addEventListener('click', (e) => { if (this.options.disabled || this.options.loading) { e.preventDefault(); return; } // Haptic feedback if (this.options.hapticFeedback && 'vibrate' in navigator) { navigator.vibrate(10); } // Visual feedback this.element.classList.add('muspe-button--pressed'); setTimeout(() => { this.element.classList.remove('muspe-button--pressed'); }, 150); if (this.callbacks.onClick) { this.callbacks.onClick(e); } }); // Touch events for better mobile experience this.element.addEventListener('touchstart', (e) => { if (!this.options.disabled && !this.options.loading) { this.element.classList.add('muspe-button--touching'); } }); this.element.addEventListener('touchend', () => { this.element.classList.remove('muspe-button--touching'); }); this.element.addEventListener('touchcancel', () => { this.element.classList.remove('muspe-button--touching'); }); } setText(text) { this.options.text = text; const textElement = this.element?.querySelector('.muspe-button__text'); if (textElement) { textElement.textContent = text; } } setLoading(loading) { this.options.loading = loading; if (!this.element) return; this.element.disabled = this.options.disabled || loading; if (loading) { this.element.classList.add('muspe-button--loading'); if (!this.element.querySelector('.muspe-button__spinner')) { this.element.insertAdjacentHTML('afterbegin', '<span class="muspe-button__spinner"></span>'); } } else { this.element.classList.remove('muspe-button--loading'); const spinner = this.element.querySelector('.muspe-button__spinner'); if (spinner) { spinner.remove(); } } } setDisabled(disabled) { this.options.disabled = disabled; if (this.element) { this.element.disabled = disabled || this.options.loading; if (disabled) { this.element.classList.add('muspe-button--disabled'); } else { this.element.classList.remove('muspe-button--disabled'); } } } destroy() { if (this.element && this.element.parentNode) { this.element.parentNode.removeChild(this.element); } this.element = null; } } // CSS Styles for Button const buttonStyles = ` .muspe-button { display: inline-flex; align-items: center; justify-content: center; gap: var(--muspe-space-2); font-family: var(--muspe-font-family); font-weight: 600; line-height: 1; text-align: center; white-space: nowrap; border: 2px solid transparent; border-radius: var(--muspe-radius-lg); cursor: pointer; user-select: none; transition: all var(--muspe-transition-fast); position: relative; overflow: hidden; } .muspe-button:focus { outline: 2px solid var(--muspe-primary); outline-offset: 2px; } .muspe-button:disabled { cursor: not-allowed; opacity: 0.6; } /* Variants */ .muspe-button--primary { background: var(--muspe-primary); color: var(--muspe-white); } .muspe-button--primary:hover:not(:disabled) { background: var(--muspe-primary-dark); } .muspe-button--secondary { background: var(--muspe-gray-100); color: var(--muspe-gray-900); } .muspe-button--secondary:hover:not(:disabled) { background: var(--muspe-gray-200); } .muspe-button--outline { background: transparent; color: var(--muspe-primary); border-color: var(--muspe-primary); } .muspe-button--outline:hover:not(:disabled) { background: var(--muspe-primary); color: var(--muspe-white); } .muspe-button--ghost { background: transparent; color: var(--muspe-primary); } .muspe-button--ghost:hover:not(:disabled) { background: var(--muspe-gray-100); } .muspe-button--danger { background: var(--muspe-error); color: var(--muspe-white); } .muspe-button--danger:hover:not(:disabled) { background: #e53e3e; } /* Sizes */ .muspe-button--sm { padding: var(--muspe-space-2) var(--muspe-space-3); font-size: var(--muspe-font-size-sm); min-height: 36px; } .muspe-button--md { padding: var(--muspe-space-3) var(--muspe-space-5); font-size: var(--muspe-font-size-base); min-height: 44px; } .muspe-button--lg { padding: var(--muspe-space-4) var(--muspe-space-6); font-size: var(--muspe-font-size-lg); min-height: 52px; } .muspe-button--xl { padding: var(--muspe-space-5) var(--muspe-space-8); font-size: var(--muspe-font-size-xl); min-height: 60px; } /* States */ .muspe-button--full { width: 100%; } .muspe-button--loading { cursor: wait; } .muspe-button--loading .muspe-button__text { opacity: 0.7; } .muspe-button--pressed, .muspe-button--touching { transform: scale(0.97); } .muspe-button:active:not(:disabled) { transform: scale(0.97); } /* Elements */ .muspe-button__icon { display: flex; align-items: center; justify-content: center; font-size: 1.2em; } .muspe-button__icon svg { width: 1em; height: 1em; stroke: currentColor; } .muspe-button__spinner { width: 16px; height: 16px; border: 2px solid transparent; border-top: 2px solid currentColor; border-radius: 50%; animation: muspe-spin 1s linear infinite; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); } @keyframes muspe-spin { to { transform: translate(-50%, -50%) rotate(360deg); } } /* Dark mode adjustments */ @media (prefers-color-scheme: dark) { .muspe-button--secondary { background: var(--muspe-gray-800); color: var(--muspe-gray-100); } .muspe-button--secondary:hover:not(:disabled) { background: var(--muspe-gray-700); } .muspe-button--ghost:hover:not(:disabled) { background: var(--muspe-gray-800); } } /* Touch device optimizations */ @media (hover: none) and (pointer: coarse) { .muspe-button { min-height: 48px; } .muspe-button--sm { min-height: 40px; } .muspe-button--lg { min-height: 56px; } .muspe-button--xl { min-height: 64px; } } /* Reduced motion */ @media (prefers-reduced-motion: reduce) { .muspe-button { transition: none; } .muspe-button__spinner { animation: none; } } `; // Auto-inject styles if (typeof document !== 'undefined') { const styleSheet = document.createElement('style'); styleSheet.textContent = buttonStyles; document.head.appendChild(styleSheet); } export default Button;