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
JavaScript
// 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 */
(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 */
(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;