fully working menu system with hughlighting now

This commit is contained in:
Mark Gregory
2026-06-15 15:03:22 +01:00
parent 91f2a7efb3
commit 068ced339e
2 changed files with 1374 additions and 441 deletions
File diff suppressed because it is too large Load Diff
+247 -81
View File
@@ -31,8 +31,10 @@
--wms-sidebar-min-width: 224px;
--wms-sidebar-max-width: 448px;
--wms-sidebar-collapsed-width: 76px;
--wms-active-parent-blue: #4164dc;
--wms-active-child-gold: #e8b52c;
--wms-active-child-gold: #e7b72f;
--wms-active-child-gold-dark: #e7b72f;
}
.wms-app-shell {
@@ -105,11 +107,15 @@
display: none;
}
.wms-sidebar-nav {
overflow-x: hidden;
}
.wms-sidebar-heading-button {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
justify-content: flex-start;
gap: 0.75rem;
border: 0;
background: transparent;
@@ -120,7 +126,7 @@
.wms-sidebar-heading-content {
display: inline-flex;
align-items: center;
gap: 0.75rem;
gap: 0.7rem;
min-width: 0;
}
@@ -131,66 +137,60 @@
}
.wms-sidebar-heading-chevron {
font-size: 0.7rem;
opacity: 0.65;
transition: transform 160ms ease, opacity 160ms ease;
}
.wms-sidebar-group[data-wms-menu-open="true"] .wms-sidebar-heading-chevron {
transform: rotate(180deg);
opacity: 1;
display: none;
}
.wms-sidebar-group-items {
display: none;
}
.wms-sidebar-group[data-wms-menu-open="true"] .wms-sidebar-group-items {
.wms-sidebar-group[data-wms-menu-open="true"] > .wms-sidebar-group-items {
display: block;
}
.wms-sidebar-group[data-wms-menu-open="true"] > .wms-sidebar-heading-button,
.wms-sidebar-group:has(.wms-sidebar-link.is-active) > .wms-sidebar-heading-button {
.wms-sidebar-group:not([data-wms-menu-group="platform"])[data-wms-selected="true"] > .wms-sidebar-heading-button {
position: relative;
margin: 0.25rem 0.5rem;
width: calc(100% - 1rem);
min-height: 3.5rem;
padding: 0.75rem 1rem;
border-radius: 9999px;
background: var(--wms-active-parent-blue);
width: calc(100% - 1.25rem);
min-height: 2.9rem;
margin: 0.25rem 0.65rem 0.35rem;
padding: 0.6rem 0.85rem;
border-radius: 1.35rem;
background: linear-gradient(135deg, #4667df 0%, #3f5fd2 100%);
color: #ffffff;
font-weight: 700;
box-shadow: 0 12px 24px rgba(65, 100, 220, 0.22);
letter-spacing: 0.035em;
box-shadow: 0 8px 18px rgba(65, 100, 220, 0.22);
}
.wms-sidebar-group[data-wms-menu-open="true"] > .wms-sidebar-heading-button .wms-sidebar-icon,
.wms-sidebar-group:has(.wms-sidebar-link.is-active) > .wms-sidebar-heading-button .wms-sidebar-icon,
.wms-sidebar-group[data-wms-menu-open="true"] > .wms-sidebar-heading-button .wms-sidebar-heading-chevron,
.wms-sidebar-group:has(.wms-sidebar-link.is-active) > .wms-sidebar-heading-button .wms-sidebar-heading-chevron {
.wms-sidebar-group:not([data-wms-menu-group="platform"])[data-wms-selected="true"] > .wms-sidebar-heading-button .wms-sidebar-icon {
color: #ffffff;
}
.wms-sidebar-link.is-active {
position: relative;
margin: 0.25rem 0.75rem 0.25rem 1.25rem;
min-height: 3.75rem;
padding: 0.75rem 1.25rem;
border-radius: 9999px 0 0 9999px;
isolation: isolate;
width: calc(100% - 2.05rem);
min-height: 2.85rem;
margin: 0.2rem 1.25rem 0.25rem 0.8rem;
padding: 0.6rem 0.95rem;
border-radius: 1.65rem 0.75rem 0.75rem 1.65rem;
background: var(--wms-active-child-gold);
color: #ffffff;
font-weight: 700;
box-shadow: 0 12px 24px rgba(232, 181, 44, 0.22);
box-shadow: 0 8px 18px rgba(231, 183, 47, 0.24);
}
.wms-sidebar-link.is-active::after {
position: absolute;
top: 0;
right: -1.25rem;
width: 2rem;
right: -1.05rem;
z-index: -1;
width: 1.75rem;
height: 100%;
content: "";
background: var(--wms-active-child-gold);
clip-path: polygon(0 0, 100% 50%, 0 100%);
clip-path: polygon(0 0, 64% 0, 100% 50%, 64% 100%, 0 100%);
border-radius: 0 0.9rem 0.9rem 0;
}
.wms-sidebar-link.is-active .wms-sidebar-icon,
@@ -210,15 +210,14 @@
pointer-events: none;
}
html[data-wms-sidebar="collapsed"] .wms-sidebar-group[data-wms-menu-open="true"] > .wms-sidebar-heading-button,
html[data-wms-sidebar="collapsed"] .wms-sidebar-group:has(.wms-sidebar-link.is-active) > .wms-sidebar-heading-button,
html[data-wms-sidebar="collapsed"] .wms-sidebar-group:not([data-wms-menu-group="platform"])[data-wms-selected="true"] > .wms-sidebar-heading-button,
html[data-wms-sidebar="collapsed"] .wms-sidebar-link.is-active {
width: 2.75rem;
min-height: 2.75rem;
margin: 0.25rem auto;
width: 3rem;
min-height: 3rem;
justify-content: center;
border-radius: 9999px;
padding: 0.75rem;
padding: 0.65rem;
}
html[data-wms-sidebar="collapsed"] .wms-sidebar-link.is-active::after {
@@ -241,16 +240,25 @@
display: none;
}
html[data-wms-sidebar="collapsed"] .wms-sidebar-heading-chevron,
html[data-wms-sidebar="collapsed"] .wms-sidebar-group-items {
display: initial;
.wms-sidebar-group:not([data-wms-menu-group="platform"])[data-wms-selected="true"] > .wms-sidebar-heading-button {
width: calc(100% - 1.25rem);
min-height: 2.85rem;
margin: 0.25rem 0.65rem 0.35rem;
padding: 0.6rem 0.85rem;
}
.wms-sidebar-link.is-active {
width: calc(100% - 2.05rem);
min-height: 2.8rem;
margin: 0.2rem 1.25rem 0.25rem 0.8rem;
padding: 0.6rem 0.95rem;
}
html[data-wms-sidebar="collapsed"] .wms-sidebar-group-items {
display: none;
}
html[data-wms-sidebar="collapsed"] .wms-sidebar-group[data-wms-menu-open="true"] .wms-sidebar-group-items {
html[data-wms-sidebar="collapsed"] .wms-sidebar-group[data-wms-menu-open="true"] > .wms-sidebar-group-items {
display: block;
}
@@ -285,13 +293,11 @@
<nav class="wms-sidebar-nav" aria-label="{{ __('Main navigation') }}">
<div class="wms-sidebar-group" data-wms-menu-group="platform">
<button type="button" class="wms-sidebar-heading wms-sidebar-heading-button" data-wms-menu-toggle data-wms-sidebar-label aria-expanded="true">
<button type="button" class="wms-sidebar-heading wms-sidebar-heading-button" data-wms-menu-toggle data-wms-sidebar-label aria-expanded="false">
<span class="wms-sidebar-heading-content">
<i class="fa-solid fa-screwdriver-wrench wms-sidebar-icon" aria-hidden="true"></i>
<span>{{ __('Platform') }}</span>
</span>
<i class="fa-solid fa-chevron-down wms-sidebar-heading-chevron" aria-hidden="true"></i>
</button>
<div class="wms-sidebar-group-items" data-wms-menu-items>
@@ -315,8 +321,6 @@
<i class="fa-solid fa-chart-simple wms-sidebar-icon" aria-hidden="true"></i>
<span>{{ __('Operations') }}</span>
</span>
<i class="fa-solid fa-chevron-down wms-sidebar-heading-chevron" aria-hidden="true"></i>
</button>
<div class="wms-sidebar-group-items" data-wms-menu-items>
@@ -397,8 +401,6 @@
<i class="fa-solid fa-headset wms-sidebar-icon" aria-hidden="true"></i>
<span>{{ __('Support') }}</span>
</span>
<i class="fa-solid fa-chevron-down wms-sidebar-heading-chevron" aria-hidden="true"></i>
</button>
<div class="wms-sidebar-group-items" data-wms-menu-items>
@@ -410,7 +412,7 @@
@endif
@if(Route::has('helpdesk.tickets.index'))
<a href="{{ route('helpdesk.tickets.index') }}" wire:navigate class="wms-sidebar-link @if(request()->routeIs('helpdesk.tickets.*')) is-active @endif" title="{{ __('Tickets') }}">
<a href="{{ route('helpdesk.tickets.index') }}" wire:navigate class="wms-sidebar-link @if(request()->routeIs('helpdesk.tickets.index') || request()->routeIs('helpdesk.tickets.show') || request()->routeIs('helpdesk.tickets.edit')) is-active @endif" title="{{ __('Tickets') }}">
<i class="fa-solid fa-ticket wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Tickets') }}</span>
</a>
@@ -424,7 +426,7 @@
@endif
@if($helpdeskKanbanRoute)
<a href="{{ route($helpdeskKanbanRoute) }}" wire:navigate class="wms-sidebar-link @if(request()->routeIs('helpdesk.kanban*')) is-active @endif" title="{{ __('Kanban') }}">
<a href="{{ route($helpdeskKanbanRoute) }}" wire:navigate class="wms-sidebar-link @if(request()->routeIs('helpdesk.kanban') || request()->routeIs('helpdesk.kanban.index')) is-active @endif" title="{{ __('Kanban') }}">
<i class="fa-solid fa-table-columns wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Kanban') }}</span>
</a>
@@ -452,7 +454,7 @@
@endif
@if($helpdeskSettingsRoute)
<a href="{{ route($helpdeskSettingsRoute) }}" wire:navigate class="wms-sidebar-link @if(request()->routeIs('helpdesk.settings*')) is-active @endif" title="{{ __('Helpdesk Settings') }}">
<a href="{{ route($helpdeskSettingsRoute) }}" wire:navigate class="wms-sidebar-link @if(request()->routeIs('helpdesk.settings') || request()->routeIs('helpdesk.settings.index')) is-active @endif" title="{{ __('Helpdesk Settings') }}">
<i class="fa-solid fa-gears wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Settings') }}</span>
</a>
@@ -467,8 +469,6 @@
<i class="fa-solid fa-plug-circle-bolt wms-sidebar-icon" aria-hidden="true"></i>
<span>{{ __('Integration & Reporting') }}</span>
</span>
<i class="fa-solid fa-chevron-down wms-sidebar-heading-chevron" aria-hidden="true"></i>
</button>
<div class="wms-sidebar-group-items" data-wms-menu-items>
@@ -508,8 +508,6 @@
<i class="fa-solid fa-sliders wms-sidebar-icon" aria-hidden="true"></i>
<span>{{ __('Administration') }}</span>
</span>
<i class="fa-solid fa-chevron-down wms-sidebar-heading-chevron" aria-hidden="true"></i>
</button>
<div class="wms-sidebar-group-items" data-wms-menu-items>
@@ -564,8 +562,6 @@
<i class="fa-solid fa-building-shield wms-sidebar-icon" aria-hidden="true"></i>
<span>{{ __('Platform Administration') }}</span>
</span>
<i class="fa-solid fa-chevron-down wms-sidebar-heading-chevron" aria-hidden="true"></i>
</button>
<div class="wms-sidebar-group-items" data-wms-menu-items>
@@ -697,18 +693,144 @@
</div>
</div>
<div
id="wms-toast-stack"
class="fixed right-4 top-4 z-[9999] flex w-[min(24rem,calc(100vw-2rem))] flex-col gap-3"
aria-live="polite"
aria-atomic="true"
></div>
@persist('toast')
<flux:toast.group>
<flux:toast />
</flux:toast.group>
@endpersist
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
window.jQuery = window.jQuery || window.$;
window.$ = window.$ || window.jQuery;
</script>
<script>
window.wmsToast = function (options) {
const stack = document.getElementById('wms-toast-stack');
if (!stack) {
return;
}
const config = {
type: options.type || 'info',
title: options.title || '',
message: options.message || '',
timeout: Number(options.timeout || 4500),
};
const palette = {
success: 'border-emerald-400/40 bg-emerald-950/95 text-emerald-50',
error: 'border-red-400/40 bg-red-950/95 text-red-50',
warning: 'border-amber-400/40 bg-amber-950/95 text-amber-50',
info: 'border-blue-400/40 bg-blue-950/95 text-blue-50',
};
const icon = {
success: 'fa-circle-check',
error: 'fa-circle-xmark',
warning: 'fa-triangle-exclamation',
info: 'fa-circle-info',
};
const toast = document.createElement('div');
toast.className = [
'wms-toast pointer-events-auto rounded-2xl border px-4 py-3 shadow-2xl backdrop-blur',
'transition duration-200 ease-out translate-y-0 opacity-100',
palette[config.type] || palette.info,
].join(' ');
toast.innerHTML = `
<div class="flex items-start gap-3">
<i class="fa-solid ${icon[config.type] || icon.info} mt-0.5 text-lg" aria-hidden="true"></i>
<div class="min-w-0 flex-1">
${config.title ? `<div class="text-sm font-bold">${config.title}</div>` : ''}
<div class="text-sm leading-5">${config.message}</div>
</div>
<button type="button" class="ml-2 text-white/70 hover:text-white" aria-label="Close notification">
<i class="fa-solid fa-xmark" aria-hidden="true"></i>
</button>
</div>
`;
const close = function () {
toast.classList.add('translate-y-[-0.35rem]', 'opacity-0');
window.setTimeout(function () {
toast.remove();
}, 200);
};
toast.querySelector('button').addEventListener('click', close);
stack.appendChild(toast);
if (config.timeout > 0) {
window.setTimeout(close, config.timeout);
}
};
window.addEventListener('wms:toast', function (event) {
window.wmsToast(event.detail || {});
});
function showSessionToasts() {
@if(session('success'))
window.wmsToast({
type: 'success',
title: @json(__('Success')),
message: @json(session('success')),
});
@endif
@if(session('error'))
window.wmsToast({
type: 'error',
title: @json(__('Error')),
message: @json(session('error')),
});
@endif
@if(session('warning'))
window.wmsToast({
type: 'warning',
title: @json(__('Warning')),
message: @json(session('warning')),
});
@endif
@if(session('info'))
window.wmsToast({
type: 'info',
title: @json(__('Information')),
message: @json(session('info')),
});
@endif
}
document.addEventListener('DOMContentLoaded', showSessionToasts);
document.addEventListener('livewire:navigated', showSessionToasts);
</script>
@fluxScripts
<script>
(function () {
const SIDEBAR_STATE_KEY = 'wms-sidebar-state';
const SIDEBAR_WIDTH_KEY = 'wms-sidebar-width';
const PLATFORM_FIRST_OPEN_KEY = 'wms-platform-first-opened';
const DEFAULT_WIDTH = 288;
const MIN_WIDTH = 224;
@@ -744,39 +866,70 @@
});
}
function getMenuItems(group) {
return Array.from(group.children).find((child) => child.matches('[data-wms-menu-items]')) || null;
}
function getMenuToggle(group) {
return Array.from(group.children).find((child) => child.matches('[data-wms-menu-toggle]')) || null;
}
function groupHasActiveLink(group) {
const items = getMenuItems(group);
return items ? items.querySelector('.wms-sidebar-link.is-active') !== null : false;
}
function setMenuGroupOpen(group, isOpen) {
group.dataset.wmsMenuOpen = isOpen ? 'true' : 'false';
const toggle = group.querySelector('[data-wms-menu-toggle]');
const toggle = getMenuToggle(group);
if (toggle) {
toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
}
}
function closeOtherNonPlatformMenus(currentGroup) {
function setMenuGroupSelected(group, isSelected) {
group.dataset.wmsSelected = isSelected ? 'true' : 'false';
group.dataset.wmsHasActive = isSelected ? 'true' : 'false';
}
function resetAllMenuGroups() {
document.querySelectorAll('[data-wms-menu-group]').forEach((group) => {
const key = group.dataset.wmsMenuGroup;
if (key === 'platform') {
return;
}
if (group === currentGroup) {
return;
}
setMenuGroupOpen(group, false);
setMenuGroupSelected(group, false);
group.dataset.wmsManualActive = 'false';
});
}
function applyMenuState() {
document.querySelectorAll('[data-wms-menu-group]').forEach((group) => {
const key = group.dataset.wmsMenuGroup;
const hasActiveLink = group.querySelector('.wms-sidebar-link.is-active') !== null;
function selectMenuGroup(group, openGroup = true) {
resetAllMenuGroups();
setMenuGroupSelected(group, true);
setMenuGroupOpen(group, openGroup);
}
setMenuGroupOpen(group, key === 'platform' || hasActiveLink);
});
function applyMenuState() {
const groups = Array.from(document.querySelectorAll('[data-wms-menu-group]'));
const selectedByRoute = groups.find((group) => groupHasActiveLink(group)) || null;
const selectedByManualClick = groups.find((group) => group.dataset.wmsManualActive === 'true') || null;
const selectedGroup = selectedByRoute || selectedByManualClick;
const shouldOpenPlatformForFirstVisit = !selectedGroup && localStorage.getItem(PLATFORM_FIRST_OPEN_KEY) !== '1';
resetAllMenuGroups();
if (selectedGroup) {
setMenuGroupSelected(selectedGroup, true);
setMenuGroupOpen(selectedGroup, true);
} else if (shouldOpenPlatformForFirstVisit) {
const platformGroup = groups.find((group) => group.dataset.wmsMenuGroup === 'platform');
if (platformGroup) {
setMenuGroupOpen(platformGroup, true);
}
localStorage.setItem(PLATFORM_FIRST_OPEN_KEY, '1');
}
}
function bindMenuControls() {
@@ -794,14 +947,15 @@
return;
}
const key = group.dataset.wmsMenuGroup;
const nextOpen = group.dataset.wmsMenuOpen !== 'true';
if (key !== 'platform' && nextOpen) {
closeOtherNonPlatformMenus(group);
}
resetAllMenuGroups();
setMenuGroupOpen(group, nextOpen);
if (nextOpen) {
group.dataset.wmsManualActive = 'true';
setMenuGroupSelected(group, true);
setMenuGroupOpen(group, true);
}
});
});
}
@@ -990,6 +1144,18 @@
link.dataset.wmsMobileBound = '1';
link.addEventListener('click', function () {
document.querySelectorAll('.wms-sidebar-link.is-active').forEach((activeLink) => {
activeLink.classList.remove('is-active');
});
link.classList.add('is-active');
const selectedGroup = link.closest('[data-wms-menu-group]');
if (selectedGroup) {
selectMenuGroup(selectedGroup, true);
}
applySidebarWidth();
closeMobileSidebar();
});