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
JavaScript
// 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%);
}
muspe-spin {
to {
transform: translate(-50%, -50%) rotate(360deg);
}
}
/* Dark mode adjustments */
(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 */
(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 */
(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;