feat: Implement a command palette for quick navigation within the administrator panel.

This commit is contained in:
pbao9
2026-02-15 01:23:01 +07:00
parent a7614c91cd
commit ab8831b71a
5 changed files with 13822 additions and 713 deletions
+13052
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -70,6 +70,7 @@
},
"dependencies": {
"animate.css": "^4.1.1",
"cmdk": "^1.1.1",
"flyonui": "^2.1.0",
"laravel-echo": "^1.16.1",
"micromodal": "^0.6.1",
@@ -237,4 +237,6 @@
</a>
</div>
<x-command-palette />
</nav>
@@ -0,0 +1,203 @@
@php
use Illuminate\Support\Facades\Route;
$sidebar = config('admin-sidebar');
$menuItems = [];
function extractRoutes($items, &$menuItems, $group = '') {
foreach ($items as $item) {
if (isset($item['type']) && $item['type'] === 'group' && isset($item['children'])) {
extractRoutes($item['children'], $menuItems, $item['title']);
} elseif (isset($item['route'])) {
$menuItems[] = [
'title' => $item['title'],
'route' => $item['route'],
'params' => $item['params'] ?? [],
'group' => $group,
];
}
}
}
extractRoutes($sidebar, $menuItems);
$validMenuItems = collect($menuItems)->filter(fn($item) => Route::has($item['route']))->values();
@endphp
<dialog id="cmdk-dialog" class="cmdk-dialog">
<div class="cmdk">
<input type="text" class="cmdk-input" placeholder="Search pages...">
<div class="cmdk-list">
@forelse($validMenuItems as $item)
<a href="{{ route($item['route'], $item['params']) }}"
class="cmdk-item"
data-value="{{ strtolower($item['title']) }}">
<span>{{ $item['title'] }}</span>
@if($item['group'])
<span class="cmdk-group">{{ $item['group'] }}</span>
@endif
</a>
@empty
<div class="cmdk-empty">No pages found</div>
@endforelse
</div>
</div>
</dialog>
@push('custom-scripts')
<script>
(function() {
const searchInput = document.getElementById('navbarForm');
const dialog = document.getElementById('cmdk-dialog');
console.log('Search input:', searchInput);
console.log('Dialog:', dialog);
if (!searchInput) {
console.error('navbarForm not found');
return;
}
if (!dialog) {
console.error('cmdk-dialog not found');
return;
}
const cmdkInput = dialog.querySelector('.cmdk-input');
const cmdkItems = dialog.querySelectorAll('.cmdk-item');
console.log('CMDK items:', cmdkItems.length);
searchInput.addEventListener('focus', function() {
console.log('Focus triggered');
dialog.showModal();
});
searchInput.addEventListener('input', function(e) {
const query = e.target.value.toLowerCase();
console.log('Search query:', query);
if (!dialog.hasAttribute('open')) {
dialog.showModal();
}
cmdkItems.forEach(function(item) {
const value = item.dataset.value || item.textContent.toLowerCase();
const match = query === '' || value.includes(query);
item.style.display = match ? '' : 'none';
});
});
document.addEventListener('keydown', function(e) {
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
dialog.showModal();
cmdkInput.focus();
}
});
dialog.addEventListener('click', function(e) {
if (e.target === dialog) {
dialog.close();
searchInput.blur();
}
});
dialog.addEventListener('close', function() {
searchInput.value = '';
cmdkItems.forEach(function(item) {
item.style.display = '';
});
});
})();
</script>
<style>
.cmdk-dialog {
padding: 0;
border: none;
background: transparent;
max-width: 700px;
width: 100%;
border-radius: 12px;
z-index: 1056;
}
.cmdk-dialog::backdrop {
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
}
.cmdk {
background: var(--bs-body-bg, #fff);
border-radius: 12px;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
overflow: hidden;
border: 1px solid var(--bs-border-color, #e2e8f0);
}
.cmdk-input {
width: 100%;
padding: 16px;
font-size: 1.125rem;
border: none;
border-bottom: 1px solid var(--bs-border-color, #e2e8f0);
background: transparent;
outline: none;
color: var(--bs-body-color, #1e293b);
}
.cmdk-list {
max-height: 400px;
overflow-y: auto;
padding: 8px;
}
.cmdk-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-radius: 8px;
text-decoration: none;
color: var(--bs-body-color, #1e293b);
cursor: pointer;
transition: background 0.15s;
}
.cmdk-item:hover,
.cmdk-item[data-selected="true"] {
background: var(--bs-tertiary-bg, #f1f5f9);
}
.cmdk-group {
font-size: 0.75rem;
color: var(--bs-secondary-color, #64748b);
}
.cmdk-empty {
padding: 24px;
text-align: center;
color: var(--bs-secondary-color, #64748b);
}
[data-bs-theme="dark"] .cmdk {
background: #1e293b;
border-color: #334155;
}
[data-bs-theme="dark"] .cmdk-input {
color: #f8fafc;
}
[data-bs-theme="dark"] .cmdk-item {
color: #f8fafc;
}
[data-bs-theme="dark"] .cmdk-item:hover,
[data-bs-theme="dark"] .cmdk-item[data-selected="true"] {
background: #334155;
}
</style>
@endpush
+564 -713
View File
File diff suppressed because it is too large Load Diff