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

464 lines (383 loc) 10.4 kB
// MusPE UI - Card Component class Card { constructor(options = {}) { this.options = { title: '', subtitle: '', content: '', image: null, actions: [], variant: 'elevated', // elevated, outlined, filled clickable: false, padding: 'default', // none, sm, default, lg ...options }; this.element = null; this.callbacks = { onClick: null, onActionClick: null, ...options.callbacks }; } render() { this.element = document.createElement('div'); this.element.className = this.buildClasses(); this.element.innerHTML = ` ${this.renderImage()} <div class="muspe-card__body"> ${this.renderHeader()} ${this.renderContent()} </div> ${this.renderActions()} `; this.attachEvents(); return this.element; } buildClasses() { const classes = ['muspe-card']; classes.push(`muspe-card--${this.options.variant}`); classes.push(`muspe-card--padding-${this.options.padding}`); if (this.options.clickable) { classes.push('muspe-card--clickable'); } return classes.join(' '); } renderImage() { if (!this.options.image) return ''; const imageContent = typeof this.options.image === 'string' ? `<img src="${this.options.image}" alt="" class="muspe-card__image" />` : this.options.image; return `<div class="muspe-card__media">${imageContent}</div>`; } renderHeader() { if (!this.options.title && !this.options.subtitle) return ''; return ` <div class="muspe-card__header"> ${this.options.title ? `<h3 class="muspe-card__title">${this.options.title}</h3>` : ''} ${this.options.subtitle ? `<p class="muspe-card__subtitle">${this.options.subtitle}</p>` : ''} </div> `; } renderContent() { if (!this.options.content) return ''; return `<div class="muspe-card__content">${this.options.content}</div>`; } renderActions() { if (!this.options.actions || this.options.actions.length === 0) return ''; const actions = this.options.actions.map((action, index) => { return ` <button class="muspe-card__action" data-action="${index}"> ${action.icon ? `<span class="muspe-card__action-icon">${action.icon}</span>` : ''} ${action.text ? `<span class="muspe-card__action-text">${action.text}</span>` : ''} </button> `; }).join(''); return `<div class="muspe-card__actions">${actions}</div>`; } attachEvents() { if (!this.element) return; // Card click if (this.options.clickable && this.callbacks.onClick) { this.element.addEventListener('click', (e) => { // Don't trigger if clicking on actions if (e.target.closest('.muspe-card__actions')) return; this.callbacks.onClick(e); }); } // Action clicks const actionButtons = this.element.querySelectorAll('.muspe-card__action'); actionButtons.forEach((button, index) => { button.addEventListener('click', (e) => { e.stopPropagation(); const action = this.options.actions[index]; if (action.callback) { action.callback(); } if (this.callbacks.onActionClick) { this.callbacks.onActionClick(action, index); } }); }); // Touch feedback for clickable cards if (this.options.clickable) { this.element.addEventListener('touchstart', () => { this.element.classList.add('muspe-card--pressed'); }); this.element.addEventListener('touchend', () => { this.element.classList.remove('muspe-card--pressed'); }); this.element.addEventListener('touchcancel', () => { this.element.classList.remove('muspe-card--pressed'); }); } } setTitle(title) { this.options.title = title; const titleElement = this.element?.querySelector('.muspe-card__title'); if (titleElement) { titleElement.textContent = title; } } setSubtitle(subtitle) { this.options.subtitle = subtitle; const subtitleElement = this.element?.querySelector('.muspe-card__subtitle'); if (subtitleElement) { subtitleElement.textContent = subtitle; } } setContent(content) { this.options.content = content; const contentElement = this.element?.querySelector('.muspe-card__content'); if (contentElement) { if (typeof content === 'string') { contentElement.innerHTML = content; } else if (content instanceof HTMLElement) { contentElement.innerHTML = ''; contentElement.appendChild(content); } } } setImage(image) { this.options.image = image; const mediaElement = this.element?.querySelector('.muspe-card__media'); if (mediaElement) { if (typeof image === 'string') { mediaElement.innerHTML = `<img src="${image}" alt="" class="muspe-card__image" />`; } else { mediaElement.innerHTML = image; } } } destroy() { if (this.element && this.element.parentNode) { this.element.parentNode.removeChild(this.element); } this.element = null; } } // CSS Styles for Card const cardStyles = ` .muspe-card { background: var(--muspe-white); border-radius: var(--muspe-radius-xl); overflow: hidden; transition: all var(--muspe-transition-fast); position: relative; } /* Variants */ .muspe-card--elevated { box-shadow: var(--muspe-shadow-md); } .muspe-card--elevated:hover { box-shadow: var(--muspe-shadow-lg); } .muspe-card--outlined { border: 1px solid var(--muspe-gray-200); } .muspe-card--filled { background: var(--muspe-gray-50); } /* Clickable */ .muspe-card--clickable { cursor: pointer; user-select: none; } .muspe-card--clickable:hover { transform: translateY(-2px); } .muspe-card--clickable:active, .muspe-card--pressed { transform: translateY(0) scale(0.98); } /* Media */ .muspe-card__media { position: relative; overflow: hidden; } .muspe-card__image { width: 100%; height: auto; display: block; object-fit: cover; } /* Body */ .muspe-card__body { display: flex; flex-direction: column; gap: var(--muspe-space-3); } /* Padding variants */ .muspe-card--padding-none .muspe-card__body { padding: 0; } .muspe-card--padding-sm .muspe-card__body { padding: var(--muspe-space-3); } .muspe-card--padding-default .muspe-card__body { padding: var(--muspe-space-5); } .muspe-card--padding-lg .muspe-card__body { padding: var(--muspe-space-6); } /* Header */ .muspe-card__header { display: flex; flex-direction: column; gap: var(--muspe-space-1); } .muspe-card__title { font-size: var(--muspe-font-size-lg); font-weight: 600; color: var(--muspe-gray-900); margin: 0; line-height: 1.3; } .muspe-card__subtitle { font-size: var(--muspe-font-size-sm); color: var(--muspe-gray-600); margin: 0; line-height: 1.4; } /* Content */ .muspe-card__content { color: var(--muspe-gray-700); line-height: 1.6; font-size: var(--muspe-font-size-base); } .muspe-card__content p { margin: 0 0 var(--muspe-space-3) 0; } .muspe-card__content p:last-child { margin-bottom: 0; } /* Actions */ .muspe-card__actions { display: flex; gap: var(--muspe-space-2); padding: var(--muspe-space-3) var(--muspe-space-5) var(--muspe-space-5); border-top: 1px solid var(--muspe-gray-200); margin-top: auto; } .muspe-card--padding-none .muspe-card__actions { padding: var(--muspe-space-3); } .muspe-card--padding-sm .muspe-card__actions { padding: var(--muspe-space-2) var(--muspe-space-3) var(--muspe-space-3); } .muspe-card--padding-lg .muspe-card__actions { padding: var(--muspe-space-4) var(--muspe-space-6) var(--muspe-space-6); } .muspe-card__action { display: inline-flex; align-items: center; gap: var(--muspe-space-2); background: transparent; color: var(--muspe-primary); border: none; padding: var(--muspe-space-2) var(--muspe-space-3); border-radius: var(--muspe-radius-md); font-size: var(--muspe-font-size-sm); font-weight: 600; cursor: pointer; transition: background-color var(--muspe-transition-fast); min-height: 36px; } .muspe-card__action:hover { background: var(--muspe-gray-100); } .muspe-card__action:active { background: var(--muspe-gray-200); transform: scale(0.98); } .muspe-card__action-icon { font-size: 1.1em; display: flex; align-items: center; } .muspe-card__action-text { white-space: nowrap; } /* Responsive adjustments */ @media (max-width: 480px) { .muspe-card__actions { flex-direction: column; gap: var(--muspe-space-1); } .muspe-card__action { justify-content: center; width: 100%; } } /* Dark mode */ @media (prefers-color-scheme: dark) { .muspe-card { background: var(--muspe-gray-800); } .muspe-card--outlined { border-color: var(--muspe-gray-700); } .muspe-card--filled { background: var(--muspe-gray-700); } .muspe-card__title { color: var(--muspe-gray-100); } .muspe-card__subtitle { color: var(--muspe-gray-400); } .muspe-card__content { color: var(--muspe-gray-300); } .muspe-card__actions { border-top-color: var(--muspe-gray-700); } .muspe-card__action:hover { background: var(--muspe-gray-700); } .muspe-card__action:active { background: var(--muspe-gray-600); } } /* Loading state */ .muspe-card--loading { position: relative; overflow: hidden; } .muspe-card--loading::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient( 90deg, transparent, rgba(255, 255, 255, 0.2), transparent ); animation: muspe-card-shimmer 1.5s infinite; } @keyframes muspe-card-shimmer { 0% { left: -100%; } 100% { left: 100%; } } /* Reduced motion */ @media (prefers-reduced-motion: reduce) { .muspe-card, .muspe-card--clickable:hover { transition: none; transform: none; } .muspe-card--loading::before { animation: none; } } `; // Auto-inject styles if (typeof document !== 'undefined') { const styleSheet = document.createElement('style'); styleSheet.textContent = cardStyles; document.head.appendChild(styleSheet); } export default Card;