Add accordion behavior to menu builder for all levels

- Categories: Only one category expanded at a time
- Items: Click item to expand/collapse modifiers
- Modifiers: Click modifier with sub-options to expand/collapse nested options
- Clicking a new parent auto-collapses siblings at that level

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
John Mizerek 2026-01-14 11:40:52 -08:00
parent a6259bccb0
commit 12b47c3e41

View file

@ -252,6 +252,27 @@
transform: rotate(90deg);
}
/* Item Toggle (for expanding modifiers) */
.item-toggle {
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: transform 0.3s ease;
color: var(--text-muted);
flex-shrink: 0;
}
.item-toggle:hover {
color: var(--text-primary);
}
.item-toggle.expanded {
transform: rotate(90deg);
}
.items-list.drag-over {
background: rgba(0, 255, 136, 0.02);
}
@ -988,6 +1009,8 @@
redoStack: [],
idCounter: 1,
expandedCategoryId: null, // For accordion - only one category expanded at a time
expandedItemId: null, // For item accordion - only one item expanded at a time
expandedModifierIds: new Set(), // Track which modifiers are expanded
// Initialize
async init() {
@ -2731,15 +2754,24 @@
return modifiers.map(mod => {
const hasOptions = mod.options && mod.options.length > 0;
const modExpanded = this.expandedModifierIds.has(mod.id);
return `
<div class="item-card modifier depth-${depth}" data-modifier-id="${mod.id}" data-parent-item-id="${parentItemId}" data-depth="${depth}"
<div class="item-card modifier depth-${depth} ${modExpanded ? 'expanded' : ''}" data-modifier-id="${mod.id}" data-parent-item-id="${parentItemId}" data-depth="${depth}"
style="margin-left: ${indent}px;"
onclick="event.stopPropagation(); MenuBuilder.selectOption('${parentItemId}', '${mod.id}', ${depth})">
${hasOptions ? `
<div class="item-toggle ${modExpanded ? 'expanded' : ''}" onclick="event.stopPropagation(); MenuBuilder.toggleModifier('${mod.id}')">
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M9 18l6-6-6-6"/>
</svg>
</div>
` : `
<div class="drag-handle" style="visibility: hidden;">
<svg width="12" height="12"></svg>
</div>
`}
<div class="item-image" style="width: ${iconSize}px; height: ${iconSize}px; font-size: ${iconSize/2}px;">${icon}</div>
<div class="item-info">
<div class="item-info" onclick="event.stopPropagation(); ${hasOptions ? `MenuBuilder.toggleModifier('${mod.id}')` : `MenuBuilder.selectOption('${parentItemId}', '${mod.id}', ${depth})`}" style="cursor: pointer;">
<div class="item-name">${this.escapeHtml(mod.name)}</div>
<div class="item-meta">
${mod.price > 0 ? `<span>+$${mod.price.toFixed(2)}</span>` : ''}
@ -2757,7 +2789,7 @@
</button>
</div>
</div>
${hasOptions ? this.renderModifiers(mod.options, mod.id, depth + 1) : ''}
${hasOptions && modExpanded ? this.renderModifiers(mod.options, mod.id, depth + 1) : ''}
`;
}).join('');
},
@ -2817,9 +2849,19 @@
<div class="items-list ${isExpanded ? '' : 'collapsed'}">
${category.items.length === 0 ? `
<div class="item-drop-zone">Drag items here or click + to add</div>
` : category.items.map(item => `
<div class="item-card" data-item-id="${item.id}" draggable="true"
` : category.items.map(item => {
const itemExpanded = this.expandedItemId === item.id;
const hasModifiers = item.modifiers && item.modifiers.length > 0;
return `
<div class="item-card ${itemExpanded ? 'expanded' : ''}" data-item-id="${item.id}" draggable="true"
onclick="event.stopPropagation(); MenuBuilder.selectElement(this)">
${hasModifiers ? `
<div class="item-toggle ${itemExpanded ? 'expanded' : ''}" onclick="event.stopPropagation(); MenuBuilder.toggleItem('${item.id}')">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M9 18l6-6-6-6"/>
</svg>
</div>
` : ''}
<div class="drag-handle">
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">
<circle cx="9" cy="6" r="1.5"/><circle cx="15" cy="6" r="1.5"/>
@ -2839,11 +2881,11 @@
</div>
` : ''}
</div>
<div class="item-info">
<div class="item-info" onclick="event.stopPropagation(); ${hasModifiers ? `MenuBuilder.toggleItem('${item.id}')` : `MenuBuilder.selectElement(this.closest('.item-card'))`}" style="cursor: pointer;">
<div class="item-name">${this.escapeHtml(item.name)}</div>
<div class="item-meta">
<span class="item-price">$${(item.price || 0).toFixed(2)}</span>
${item.modifiers.length > 0 ? `<span>${item.modifiers.length} modifiers</span>` : ''}
${hasModifiers ? `<span>${item.modifiers.length} modifiers</span>` : ''}
</div>
</div>
<div class="item-actions">
@ -2860,8 +2902,8 @@
</button>
</div>
</div>
${this.renderModifiers(item.modifiers, item.id, 1)}
`).join('')}
${itemExpanded ? this.renderModifiers(item.modifiers, item.id, 1) : ''}
`}).join('')}
</div>
</div>
`}).join('');
@ -2998,9 +3040,34 @@
// Toggle category expanded/collapsed (accordion behavior)
toggleCategory(categoryId) {
// If clicking the already expanded category, collapse it
// Otherwise expand the clicked one (auto-collapses any other)
this.expandedCategoryId = (this.expandedCategoryId === categoryId) ? null : categoryId;
if (this.expandedCategoryId === categoryId) {
this.expandedCategoryId = null;
} else {
this.expandedCategoryId = categoryId;
this.expandedItemId = null;
this.expandedModifierIds.clear();
}
this.render();
},
// Toggle item expanded/collapsed
toggleItem(itemId) {
if (this.expandedItemId === itemId) {
this.expandedItemId = null;
} else {
this.expandedItemId = itemId;
this.expandedModifierIds.clear();
}
this.render();
},
// Toggle modifier expanded/collapsed
toggleModifier(modifierId) {
if (this.expandedModifierIds.has(modifierId)) {
this.expandedModifierIds.delete(modifierId);
} else {
this.expandedModifierIds.add(modifierId);
}
this.render();
},