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
JavaScript
// 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 */
(max-width: 480px) {
.muspe-card__actions {
flex-direction: column;
gap: var(--muspe-space-1);
}
.muspe-card__action {
justify-content: center;
width: 100%;
}
}
/* Dark mode */
(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;
}
muspe-card-shimmer {
0% { left: -100%; }
100% { left: 100%; }
}
/* Reduced motion */
(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;